├── ViperWeather
├── Assets.xcassets
│ ├── Contents.json
│ └── AppIcon.appiconset
│ │ ├── Icon-76.png
│ │ ├── Icon-60@2x.png
│ │ ├── Icon-60@3x.png
│ │ ├── Icon-76@2x.png
│ │ ├── Icon-Small.png
│ │ ├── Icon-83.5@2x.png
│ │ ├── Icon-Small-40.png
│ │ ├── Icon-Small@2x-1.png
│ │ ├── Icon-Small@2x.png
│ │ ├── Icon-Small@3x.png
│ │ ├── Icon-Small-40@2x.png
│ │ ├── Icon-Small-40@3x.png
│ │ ├── Icon-Small-40@2x-1.png
│ │ └── Contents.json
├── ViperWeather-Bridging-Header.h
├── Constants.swift
├── UITextField+SwiftAppearance.h
├── Weather.swift
├── UITextField+SwiftAppearance.m
├── ServiceLocatorAssembler.swift
├── DetailRouter.swift
├── RootInteractor.swift
├── ServiceLocatorContainer.swift
├── RouterHelper.swift
├── ChildRouter.swift
├── ChildDataManager.swift
├── ChildInteractor.swift
├── City.swift
├── ListInteractor.swift
├── DetailListDetailDataManager.swift
├── DetailContainerDataManager.swift
├── DetailListDetailInteractor.swift
├── DetailContainerInteractor.swift
├── NavigationController.swift
├── ChildViewController.swift
├── RootRouter.swift
├── AddRouter.swift
├── RootAssembler.swift
├── ChildPresenter.swift
├── Root.storyboard
├── DetailContainerViewController.swift
├── DetailListDetailRouter.swift
├── AddInteractor.swift
├── CityTableViewCell.swift
├── ChildAssembler.swift
├── RootPresenter.swift
├── DetailListRouter.swift
├── Base.lproj
│ ├── Main.storyboard
│ └── LaunchScreen.storyboard
├── DetailListDetailAssembler.swift
├── WeatherTableViewCell.swift
├── DetailInteractor.swift
├── DetailListDetailPresenter.swift
├── DetailListDetailViewController.swift
├── RootViewController.swift
├── DetailListInteractor.swift
├── ListRouter.swift
├── AddAssembler.swift
├── ListPresenter.swift
├── ListDataManager.swift
├── RootContainer.swift
├── DetailContainerRouter.swift
├── DetailContainer.swift
├── DetailContainerAssembler.swift
├── DetailContainerPresenter.swift
├── DetailPresenter.swift
├── ListAssembler.swift
├── ChildContainer.swift
├── AddContainer.swift
├── ListTableViewController.xib
├── AddTableViewController.xib
├── DetailListViewController.xib
├── DetailListPresenter.swift
├── DetailAssembler.swift
├── DetailListDetailContainer.swift
├── DetailListContainer.swift
├── ListContainer.swift
├── AddDataManager.swift
├── AddPresenter.swift
├── DetailListAssembler.swift
├── DetailContainerContainer.swift
├── Info.plist
├── List.storyboard
├── RootViewController.xib
├── MaterialColor.swift
├── DetailViewController.swift
├── DetailListViewController.swift
├── DetailDataManager.swift
├── AppDelegate.swift
├── DetailListDataManager.swift
├── AddTableViewController.swift
├── WeatherTableViewCell.xib
├── DetailViewController.xib
├── Detail.storyboard
├── CityTableViewCell.xib
└── ListTableViewController.swift
├── ViperWeather.xcodeproj
└── project.xcworkspace
│ └── contents.xcworkspacedata
├── ViperWeather.xcworkspace
└── contents.xcworkspacedata
├── Podfile
├── Podfile.lock
├── README.md
├── .gitignore
└── LICENSE
/ViperWeather/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/ViperWeather/Assets.xcassets/AppIcon.appiconset/Icon-76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoolCodeFactory/ViperWeather/HEAD/ViperWeather/Assets.xcassets/AppIcon.appiconset/Icon-76.png
--------------------------------------------------------------------------------
/ViperWeather/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoolCodeFactory/ViperWeather/HEAD/ViperWeather/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png
--------------------------------------------------------------------------------
/ViperWeather/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoolCodeFactory/ViperWeather/HEAD/ViperWeather/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png
--------------------------------------------------------------------------------
/ViperWeather/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoolCodeFactory/ViperWeather/HEAD/ViperWeather/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png
--------------------------------------------------------------------------------
/ViperWeather/Assets.xcassets/AppIcon.appiconset/Icon-Small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoolCodeFactory/ViperWeather/HEAD/ViperWeather/Assets.xcassets/AppIcon.appiconset/Icon-Small.png
--------------------------------------------------------------------------------
/ViperWeather/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoolCodeFactory/ViperWeather/HEAD/ViperWeather/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png
--------------------------------------------------------------------------------
/ViperWeather/Assets.xcassets/AppIcon.appiconset/Icon-Small-40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoolCodeFactory/ViperWeather/HEAD/ViperWeather/Assets.xcassets/AppIcon.appiconset/Icon-Small-40.png
--------------------------------------------------------------------------------
/ViperWeather/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoolCodeFactory/ViperWeather/HEAD/ViperWeather/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x-1.png
--------------------------------------------------------------------------------
/ViperWeather/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoolCodeFactory/ViperWeather/HEAD/ViperWeather/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png
--------------------------------------------------------------------------------
/ViperWeather/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoolCodeFactory/ViperWeather/HEAD/ViperWeather/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png
--------------------------------------------------------------------------------
/ViperWeather/Assets.xcassets/AppIcon.appiconset/Icon-Small-40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoolCodeFactory/ViperWeather/HEAD/ViperWeather/Assets.xcassets/AppIcon.appiconset/Icon-Small-40@2x.png
--------------------------------------------------------------------------------
/ViperWeather/Assets.xcassets/AppIcon.appiconset/Icon-Small-40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoolCodeFactory/ViperWeather/HEAD/ViperWeather/Assets.xcassets/AppIcon.appiconset/Icon-Small-40@3x.png
--------------------------------------------------------------------------------
/ViperWeather/Assets.xcassets/AppIcon.appiconset/Icon-Small-40@2x-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CoolCodeFactory/ViperWeather/HEAD/ViperWeather/Assets.xcassets/AppIcon.appiconset/Icon-Small-40@2x-1.png
--------------------------------------------------------------------------------
/ViperWeather/ViperWeather-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | //
2 | // Use this file to import your target's public headers that you would like to expose to Swift.
3 | //
4 |
5 | #import "UITextField+SwiftAppearance.h"
6 |
--------------------------------------------------------------------------------
/ViperWeather.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ViperWeather.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/ViperWeather/Constants.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Constants.swift
3 | // ViperWeather
4 | //
5 | // Created by Dmitriy Utmanov on 02/03/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | let googleMapKey = <#googleMapKey#>
12 | let openWeatherMapKey = <#openWeatherMapKey#>
13 |
--------------------------------------------------------------------------------
/ViperWeather/UITextField+SwiftAppearance.h:
--------------------------------------------------------------------------------
1 | //
2 | // UITextField+SwiftAppearance.h
3 | // ViperWeather
4 | //
5 | // Created by Dmitri Utmanov on 03/03/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface UITextField (SwiftAppearance)
12 |
13 | + (instancetype)appearanceWhenContainedWithin:(Class)containerClass;
14 |
15 | @end
16 |
--------------------------------------------------------------------------------
/ViperWeather/Weather.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Weather.swift
3 | // ViperWeather
4 | //
5 | // Created by Dmitriy Utmanov on 03/03/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import RealmSwift
11 |
12 | struct Weather {
13 | let dt: Double
14 | let temp: Double
15 | let pressure: Double
16 | let icon: String
17 |
18 | var tempString: String {
19 | return String(temp) + "℃"
20 | }
21 | }
--------------------------------------------------------------------------------
/ViperWeather/UITextField+SwiftAppearance.m:
--------------------------------------------------------------------------------
1 | //
2 | // UITextField+SwiftAppearance.m
3 | // ViperWeather
4 | //
5 | // Created by Dmitri Utmanov on 03/03/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 |
9 | #import "UITextField+SwiftAppearance.h"
10 |
11 | @implementation UITextField (SwiftAppearance)
12 |
13 | + (instancetype)appearanceWhenContainedWithin:(Class)containerClass {
14 | if ([self conformsToProtocol:@protocol(UIAppearance)]) {
15 | return [self appearanceWhenContainedIn:containerClass, nil];
16 | }
17 | return nil;
18 | }
19 |
20 | @end
21 |
--------------------------------------------------------------------------------
/ViperWeather/ServiceLocatorAssembler.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ServiceLocatorAssembler.swift
3 | // Architecture
4 | //
5 | // Created Dmitri Utmanov on 24/02/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 | // Generated by Swift-Viper templates. Find latest version at https://github.com/Nikita2k/SwiftViper
9 | //
10 |
11 | import Foundation
12 | import Swinject
13 |
14 | class ServiceLocatorAssembler: Assembler {
15 |
16 | init() {
17 | let container = Container(parent: nil)
18 | super.init(container: container)
19 |
20 | self.applyAssembly(ServiceLocatorContainer())
21 | }
22 |
23 | }
--------------------------------------------------------------------------------
/ViperWeather/DetailRouter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DetailRouter.swift
3 | // ViperWeather
4 | //
5 | // Created by Dmitriy Utmanov on 29/02/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Swinject
11 |
12 |
13 | protocol DetailRouterInputProtocol: class {
14 | func dismissDetailViewController(viewController viewController: UIViewController)
15 | }
16 |
17 | protocol DetailParentRouterProtocol: class {
18 |
19 | }
20 |
21 | class DetailRouter: DetailRouterInputProtocol {
22 |
23 | func dismissDetailViewController(viewController viewController: UIViewController) {
24 | viewController.dismissViewControllerAnimated(true, completion: nil)
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Podfile:
--------------------------------------------------------------------------------
1 | # Uncomment this line to define a global platform for your project
2 | platform :ios, '8.0'
3 | # Uncomment this line if you're using Swift
4 | use_frameworks!
5 |
6 | target 'ViperWeather' do
7 |
8 |
9 | pod 'Swinject', '~> 1.1.0'
10 | pod 'Alamofire', '~> 3.5.0'
11 |
12 | pod 'RealmSwift', '~> 0.98.5'
13 | pod 'SwiftFetchedResultsController', '~> 3.0.1'
14 |
15 | pod 'SwiftyJSON', '~> 2.3.2'
16 |
17 | # Will used soon
18 | pod 'SnapKit', '~> 0.22.0'
19 |
20 | end
21 |
22 | # Use Legacy Swift Language Version 2.3
23 | post_install do |installer|
24 | installer.pods_project.targets.each do |target|
25 | target.build_configurations.each do |configuration|
26 | configuration.build_settings['SWIFT_VERSION'] = "2.3"
27 | end
28 | end
29 | end
--------------------------------------------------------------------------------
/ViperWeather/RootInteractor.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RootInteractor.swift
3 | // Architecture
4 | //
5 | // Created Dmitri Utmanov on 20/02/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 | // Generated by Swift-Viper templates. Find latest version at https://github.com/Nikita2k/SwiftViper
9 | //
10 |
11 | import Foundation
12 |
13 | protocol RootInteractorInputProtocol: class {
14 |
15 | weak var presenter: RootInteractorOutputProtocol! { get set }
16 |
17 | }
18 |
19 | protocol RootInteractorOutputProtocol: class {
20 |
21 | }
22 |
23 | class RootInteractor {
24 |
25 | weak var presenter: RootInteractorOutputProtocol!
26 |
27 | }
28 |
29 | extension RootInteractor: RootInteractorInputProtocol {
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/ViperWeather/ServiceLocatorContainer.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ServiceLocatorContainer.swift
3 | // Architecture
4 | //
5 | // Created Dmitri Utmanov on 24/02/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 | // Generated by Swift-Viper templates. Find latest version at https://github.com/Nikita2k/SwiftViper
9 | //
10 |
11 | import Foundation
12 | import Swinject
13 |
14 | class ServiceLocatorContainer: AssemblyType {
15 |
16 | func assemble(container: Container) {
17 | container.register(RootAssembler.self) { r in
18 | let delegete = (UIApplication.sharedApplication().delegate as! AppDelegate)
19 | return RootAssembler(parentAssembler: delegete.serviceLocatorAssembler)
20 | }
21 | }
22 |
23 | }
--------------------------------------------------------------------------------
/ViperWeather/RouterHelper.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RootHelper.swift
3 | // Architecture
4 | //
5 | // Created by Dmitriy Utmanov on 26/02/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 |
12 | class RouterHelper {
13 |
14 | class func navigationController() -> UINavigationController {
15 | let delegate = UIApplication.sharedApplication().delegate as! AppDelegate
16 | let rootNVC = delegate.window!.rootViewController as! UINavigationController
17 | return rootNVC
18 | }
19 |
20 | class func setRootViewController(viewController: UIViewController, animated: Bool) {
21 | self.navigationController().setViewControllers([viewController], animated: animated)
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/ViperWeather/ChildRouter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ChildRouter.swift
3 | // ViperWeather
4 | //
5 | // Created Dima on 16/03/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 | // Generated via Swift-Viper templates by https://github.com/cooler333
9 | //
10 |
11 | import UIKit
12 | import Swinject
13 |
14 |
15 | protocol ChildRouterInputProtocol: class {
16 | func dismissChildViewController(viewController viewController: UIViewController)
17 | }
18 |
19 | protocol ChildParentRouterProtocol: class {
20 |
21 | }
22 |
23 | class ChildRouter: ChildRouterInputProtocol {
24 |
25 | func dismissChildViewController(viewController viewController: UIViewController) {
26 | viewController.dismissViewControllerAnimated(true, completion: nil)
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/ViperWeather/ChildDataManager.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ChildDataManager.swift
3 | // ViperWeather
4 | //
5 | // Created Dima on 16/03/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 | // Generated via Swift-Viper templates by https://github.com/cooler333
9 | //
10 |
11 | import Foundation
12 | import RealmSwift
13 | import Alamofire
14 |
15 |
16 | protocol ChildDataManagerInputProtocol: class {
17 |
18 | weak var interactor: ChildDataManagerOutputProtocol! { get set }
19 | }
20 |
21 | protocol ChildDataManagerOutputProtocol: class {
22 |
23 | var dataManager: ChildDataManagerInputProtocol! { get set }
24 | }
25 |
26 |
27 | class ChildDataManager {
28 |
29 | weak var interactor: ChildDataManagerOutputProtocol!
30 | }
31 |
32 | extension ChildDataManager: ChildDataManagerInputProtocol {
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/ViperWeather/ChildInteractor.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ChildInteractor.swift
3 | // ViperWeather
4 | //
5 | // Created Dima on 16/03/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 | // Generated via Swift-Viper templates by https://github.com/cooler333
9 | //
10 |
11 | import Foundation
12 |
13 |
14 | protocol ChildInteractorInputProtocol: class {
15 |
16 | weak var presenter: ChildInteractorOutputProtocol! { get set }
17 | }
18 |
19 | protocol ChildInteractorOutputProtocol: class {
20 |
21 | }
22 |
23 | class ChildInteractor {
24 |
25 | weak var presenter: ChildInteractorOutputProtocol!
26 |
27 | var dataManager: ChildDataManagerInputProtocol!
28 | }
29 |
30 | extension ChildInteractor: ChildInteractorInputProtocol {
31 |
32 | }
33 |
34 | extension ChildInteractor: ChildDataManagerOutputProtocol {
35 |
36 | }
--------------------------------------------------------------------------------
/ViperWeather/City.swift:
--------------------------------------------------------------------------------
1 | //
2 | // City.swift
3 | // ViperWeather
4 | //
5 | // Created by Dmitriy Utmanov on 01/03/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import RealmSwift
11 |
12 | struct City {
13 | let title: String
14 | let ID: String
15 | let placeID: String
16 | var lat: Double = 0.0
17 | var lng: Double = 0.0
18 |
19 | func isLocationEnable() -> Bool {
20 | if self.lat == 0.0 && self.lng == 0.0 {
21 | return false
22 | }
23 | return true
24 | }
25 | }
26 |
27 |
28 | class CityEntity: Object {
29 | dynamic var title: String = ""
30 | dynamic var ID: String = ""
31 | dynamic var placeID: String = ""
32 | dynamic var lat: Double = 0.0
33 | dynamic var lng: Double = 0.0
34 |
35 | override static func primaryKey() -> String? {
36 | return "placeID"
37 | }
38 | }
--------------------------------------------------------------------------------
/ViperWeather/ListInteractor.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ListInteractor.swift
3 | // ViperWeather
4 | //
5 | // Created by Dmitriy Utmanov on 29/02/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 |
12 | protocol ListInteractorInputProtocol: class {
13 |
14 | weak var presenter: ListInteractorOutputProtocol! { get set }
15 |
16 | func removeCity(city: City)
17 | }
18 |
19 | protocol ListInteractorOutputProtocol: class {
20 |
21 | }
22 |
23 | class ListInteractor {
24 |
25 | weak var presenter: ListInteractorOutputProtocol!
26 |
27 | var dataManager: ListDataManagerInputProtocol!
28 | }
29 |
30 | extension ListInteractor: ListInteractorInputProtocol {
31 |
32 | func removeCity(city: City) {
33 | dataManager.removeCityFromPersistentStore(city)
34 | }
35 | }
36 |
37 | extension ListInteractor: ListDataManagerOutputProtocol {
38 |
39 | }
--------------------------------------------------------------------------------
/ViperWeather/DetailListDetailDataManager.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DetailListDetailDataManager.swift
3 | // ViperWeather
4 | //
5 | // Created Dima on 16/03/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 | // Generated via Swift-Viper templates by https://github.com/cooler333
9 | //
10 |
11 | import Foundation
12 | import RealmSwift
13 | import Alamofire
14 |
15 |
16 | protocol DetailListDetailDataManagerInputProtocol: class {
17 |
18 | weak var interactor: DetailListDetailDataManagerOutputProtocol! { get set }
19 | }
20 |
21 | protocol DetailListDetailDataManagerOutputProtocol: class {
22 |
23 | var dataManager: DetailListDetailDataManagerInputProtocol! { get set }
24 | }
25 |
26 |
27 | class DetailListDetailDataManager {
28 |
29 | weak var interactor: DetailListDetailDataManagerOutputProtocol!
30 | }
31 |
32 | extension DetailListDetailDataManager: DetailListDetailDataManagerInputProtocol {
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/ViperWeather/DetailContainerDataManager.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DetailContainerDataManager.swift
3 | // ViperWeather
4 | //
5 | // Created Dmitri Utmanov on 03/03/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 | // Generated via Swift-Viper templates by https://github.com/cooler333
9 | //
10 |
11 | import Foundation
12 | import RealmSwift
13 | import Alamofire
14 |
15 |
16 | protocol DetailContainerDataManagerInputProtocol: class {
17 |
18 | weak var interactor: DetailContainerDataManagerOutputProtocol! { get set }
19 | }
20 |
21 | protocol DetailContainerDataManagerOutputProtocol: class {
22 |
23 | var dataManager: DetailContainerDataManagerInputProtocol! { get set }
24 | }
25 |
26 |
27 | class DetailContainerDataManager {
28 |
29 | weak var interactor: DetailContainerDataManagerOutputProtocol!
30 |
31 | }
32 |
33 | extension DetailContainerDataManager: DetailContainerDataManagerInputProtocol {
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/ViperWeather/DetailListDetailInteractor.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DetailListDetailInteractor.swift
3 | // ViperWeather
4 | //
5 | // Created Dima on 16/03/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 | // Generated via Swift-Viper templates by https://github.com/cooler333
9 | //
10 |
11 | import Foundation
12 |
13 |
14 | protocol DetailListDetailInteractorInputProtocol: class {
15 |
16 | weak var presenter: DetailListDetailInteractorOutputProtocol! { get set }
17 | }
18 |
19 | protocol DetailListDetailInteractorOutputProtocol: class {
20 |
21 | }
22 |
23 | class DetailListDetailInteractor {
24 |
25 | weak var presenter: DetailListDetailInteractorOutputProtocol!
26 |
27 | var dataManager: DetailListDetailDataManagerInputProtocol!
28 | }
29 |
30 | extension DetailListDetailInteractor: DetailListDetailInteractorInputProtocol {
31 |
32 | }
33 |
34 | extension DetailListDetailInteractor: DetailListDetailDataManagerOutputProtocol {
35 |
36 | }
--------------------------------------------------------------------------------
/ViperWeather/DetailContainerInteractor.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DetailContainerInteractor.swift
3 | // ViperWeather
4 | //
5 | // Created Dmitri Utmanov on 03/03/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 | // Generated via Swift-Viper templates by https://github.com/cooler333
9 | //
10 |
11 | import Foundation
12 |
13 |
14 | protocol DetailContainerInteractorInputProtocol: class {
15 |
16 | weak var presenter: DetailContainerInteractorOutputProtocol! { get set }
17 | }
18 |
19 | protocol DetailContainerInteractorOutputProtocol: class {
20 |
21 | }
22 |
23 | class DetailContainerInteractor {
24 |
25 | weak var presenter: DetailContainerInteractorOutputProtocol!
26 |
27 | var dataManager: DetailContainerDataManagerInputProtocol!
28 |
29 | }
30 |
31 | extension DetailContainerInteractor: DetailContainerInteractorInputProtocol {
32 |
33 | }
34 |
35 | extension DetailContainerInteractor: DetailContainerDataManagerOutputProtocol {
36 |
37 | }
--------------------------------------------------------------------------------
/ViperWeather/NavigationController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NavigationController.swift
3 | // ViperWeather
4 | //
5 | // Created by Dmitri Utmanov on 02/03/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class NavigationController: UINavigationController {
12 |
13 | override func viewDidLoad() {
14 | super.viewDidLoad()
15 | }
16 |
17 | override func didReceiveMemoryWarning() {
18 | super.didReceiveMemoryWarning()
19 | // Dispose of any resources that can be recreated.
20 | }
21 |
22 |
23 | /*
24 | // MARK: - Navigation
25 |
26 | // In a storyboard-based application, you will often want to do a little preparation before navigation
27 | override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
28 | // Get the new view controller using segue.destinationViewController.
29 | // Pass the selected object to the new view controller.
30 | }
31 | */
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/ViperWeather/ChildViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ChildViewController.swift
3 | // ViperWeather
4 | //
5 | // Created Dima on 16/03/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 | // Generated via Swift-Viper templates by https://github.com/cooler333
9 | //
10 |
11 | import UIKit
12 |
13 |
14 | class ChildViewController: UIViewController {
15 |
16 | // MARK: - VIPER Properties
17 | var presenter: ChildPresenterProtocol!
18 |
19 |
20 | override func viewDidLoad() {
21 | super.viewDidLoad()
22 | // Do any additional setup after loading the view.
23 | }
24 |
25 | override func viewWillAppear(animated: Bool) {
26 | super.viewWillAppear(animated)
27 | // Update data.
28 | }
29 |
30 | override func didReceiveMemoryWarning() {
31 | super.didReceiveMemoryWarning()
32 | // Dispose of any resources that can be recreated.
33 | }
34 |
35 | }
36 |
37 | extension ChildViewController: ChildInterfaceProtocol {
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/ViperWeather/RootRouter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RootRouter.swift
3 | // Architecture
4 | //
5 | // Created Dmitri Utmanov on 20/02/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 | // Generated by Swift-Viper templates. Find latest version at https://github.com/Nikita2k/SwiftViper
9 | //
10 |
11 | import UIKit
12 | import Swinject
13 |
14 |
15 | protocol RootRouterInputProtocol: class {
16 | func openListViewController(fromViewController fromViewController: UIViewController)
17 |
18 | var listAssembler: ListAssembler! { get set }
19 | }
20 |
21 | protocol RootParentRouterProtocol: class {
22 |
23 | }
24 |
25 | class RootRouter: RootRouterInputProtocol {
26 |
27 | var listAssembler: ListAssembler!
28 |
29 | func openListViewController(fromViewController fromViewController: UIViewController) {
30 | listAssembler.presentListViewController(fromViewController: fromViewController)
31 | }
32 |
33 | }
34 |
35 | extension RootRouter: ListParentRouterProtocol {
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/ViperWeather/AddRouter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AddRouter.swift
3 | // Architecture
4 | //
5 | // Created Dmitri Utmanov on 19/02/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 | // Generated by Swift-Viper templates. Find latest version at https://github.com/Nikita2k/SwiftViper
9 | //
10 |
11 | import UIKit
12 |
13 | protocol AddRouterInputProtocol: class {
14 | func closeAddViewController(viewController viewController: UIViewController)
15 | }
16 |
17 | protocol AddParentRouterProtocol: class {
18 |
19 | }
20 |
21 | class AddRouter: AddRouterInputProtocol {
22 |
23 | func closeAddViewController(viewController viewController: UIViewController) {
24 | let idiom = UIDevice.currentDevice().userInterfaceIdiom
25 | switch idiom {
26 | case .Phone:
27 | viewController.navigationController!.dismissViewControllerAnimated(true, completion: nil)
28 |
29 | case .Pad:
30 | viewController.navigationController!.popViewControllerAnimated(true)
31 |
32 | default:
33 | fatalError("Device is not supported yet")
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/ViperWeather/RootAssembler.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RootAssembly.swift
3 | // Architecture
4 | //
5 | // Created by Dmitri Utmanov on 20/02/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Swinject
11 |
12 |
13 | class RootAssembler: Assembler {
14 |
15 | init(parentAssembler: Assembler) {
16 | try! super.init(assemblies: [RootContainer()], parentAssembler: parentAssembler)
17 | }
18 |
19 | }
20 |
21 | extension RootAssembler {
22 |
23 | func presentRootViewController(fromViewController fromViewController: UIViewController) {
24 | guard let navigationController = fromViewController as? UINavigationController else {
25 | return
26 | }
27 | let viewController = storyboard().instantiateInitialViewController()! as! RootViewController
28 |
29 | navigationController.pushViewController(viewController, animated: false)
30 | }
31 |
32 | func storyboard() -> SwinjectStoryboard {
33 | return SwinjectStoryboard.create(name: "Root", bundle: NSBundle.mainBundle(), container: resolver)
34 | }
35 |
36 | }
--------------------------------------------------------------------------------
/ViperWeather/ChildPresenter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ChildPresenter.swift
3 | // ViperWeather
4 | //
5 | // Created Dima on 16/03/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 | // Generated via Swift-Viper templates by https://github.com/cooler333
9 | //
10 |
11 | import UIKit
12 | import Swinject
13 |
14 |
15 | protocol ChildPresenterProtocol: class {
16 |
17 | }
18 |
19 | protocol ChildInterfaceProtocol: class {
20 |
21 | var presenter: ChildPresenterProtocol! { get set }
22 | }
23 |
24 | class ChildPresenter {
25 |
26 | weak private var interface: ChildInterfaceProtocol!
27 | private let interactor: ChildInteractorInputProtocol
28 | private let router: ChildRouterInputProtocol
29 |
30 |
31 | init(interface: ChildInterfaceProtocol, interactor: ChildInteractorInputProtocol, router: ChildRouterInputProtocol) {
32 | self.interface = interface
33 | self.interactor = interactor
34 | self.router = router
35 | }
36 |
37 | }
38 |
39 |
40 | extension ChildPresenter: ChildPresenterProtocol {
41 |
42 | }
43 |
44 | extension ChildPresenter: ChildInteractorOutputProtocol {
45 |
46 | }
47 |
48 |
--------------------------------------------------------------------------------
/ViperWeather/Root.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - Alamofire (3.5.1)
3 | - RBQFetchedResultsController (3.0.1):
4 | - Realm (>= 0.95)
5 | - Realm (0.98.5):
6 | - Realm/Headers (= 0.98.5)
7 | - Realm/Headers (0.98.5)
8 | - RealmSwift (0.98.5):
9 | - Realm (= 0.98.5)
10 | - SnapKit (0.22.0)
11 | - SwiftFetchedResultsController (3.0.1):
12 | - RBQFetchedResultsController (>= 3.0.1)
13 | - RealmSwift (>= 0.96)
14 | - SwiftyJSON (2.3.2)
15 | - Swinject (1.1.0)
16 |
17 | DEPENDENCIES:
18 | - Alamofire (~> 3.5.0)
19 | - RealmSwift
20 | - SnapKit (~> 0.22.0)
21 | - SwiftFetchedResultsController
22 | - SwiftyJSON
23 | - Swinject
24 |
25 | SPEC CHECKSUMS:
26 | Alamofire: 0dfba1184a543e2aa160f4e39cac4e8aba48d223
27 | RBQFetchedResultsController: 4b8a80647859241abdb3da04b486a6ad02dd5093
28 | Realm: db08379969e45cea2edd1c3c0a090fe9e22d2d2f
29 | RealmSwift: cb11ad1c35e15dc982f2dbf5afe2a3124193a358
30 | SnapKit: 0dd2fd157330f1ea11fd84da13e6be8a7a22bae0
31 | SwiftFetchedResultsController: 646da58a5cde2293b692f5c8283d1db001d720bc
32 | SwiftyJSON: 04ccea08915aa0109039157c7974cf0298da292a
33 | Swinject: 514e56e46e3cd362f2cca07bb6ad1fdbed9abbfa
34 |
35 | PODFILE CHECKSUM: e50c0a70a4092310dc9579bc3e1802498e9b9af2
36 |
37 | COCOAPODS: 1.1.1
38 |
--------------------------------------------------------------------------------
/ViperWeather/DetailContainerViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DetailContainerViewController.swift
3 | // ViperWeather
4 | //
5 | // Created by Dmitriy Utmanov on 03/03/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class DetailContainerViewController: UIViewController {
12 |
13 | // MARK: - VIPER Properties
14 | var presenter: DetailContainerPresenterProtocol!
15 |
16 | var city: City!
17 |
18 | @IBOutlet weak var detailContentView: UIView!
19 | @IBOutlet weak var detailListContentView: UIView!
20 |
21 |
22 | override func viewDidLoad() {
23 | super.viewDidLoad()
24 |
25 | // Do any additional setup after loading the view.
26 | self.title = city.title
27 |
28 | self.presenter.presentDetailViewControllerInView(detailContentView, city: city)
29 | self.presenter.presentDetailListViewControllerInView(detailListContentView, city: city)
30 | }
31 |
32 | override func didReceiveMemoryWarning() {
33 | super.didReceiveMemoryWarning()
34 | // Dispose of any resources that can be recreated.
35 | }
36 | }
37 |
38 | extension DetailContainerViewController: DetailContainerInterfaceProtocol {
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/ViperWeather/DetailListDetailRouter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DetailListDetailRouter.swift
3 | // ViperWeather
4 | //
5 | // Created Dima on 16/03/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 | // Generated via Swift-Viper templates by https://github.com/cooler333
9 | //
10 |
11 | import UIKit
12 | import Swinject
13 |
14 |
15 | protocol DetailListDetailRouterInputProtocol: class {
16 | func dismissDetailListDetailViewController(viewController viewController: UIViewController)
17 | func presentChildViewController(fromViewController fromViewController: UIViewController)
18 |
19 | var childAssembler: ChildAssembler! { get set }
20 | }
21 |
22 | protocol DetailListDetailParentRouterProtocol: class {
23 |
24 | }
25 |
26 | class DetailListDetailRouter: DetailListDetailRouterInputProtocol {
27 |
28 | var childAssembler: ChildAssembler!
29 |
30 |
31 | func dismissDetailListDetailViewController(viewController viewController: UIViewController) {
32 | viewController.dismissViewControllerAnimated(true, completion: nil)
33 | }
34 |
35 | func presentChildViewController(fromViewController fromViewController: UIViewController) {
36 | childAssembler.presentChildViewController(fromViewController: fromViewController)
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/ViperWeather/AddInteractor.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AddInteractor.swift
3 | // Architecture
4 | //
5 | // Created Dmitri Utmanov on 19/02/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 | // Generated by Swift-Viper templates. Find latest version at https://github.com/Nikita2k/SwiftViper
9 | //
10 |
11 | import Foundation
12 |
13 |
14 | protocol AddInteractorInputProtocol: class {
15 |
16 | weak var presenter: AddInteractorOutputProtocol! { get set }
17 |
18 | func getCitiesWithName(name: String)
19 | func saveCity(city: City)
20 | }
21 |
22 | protocol AddInteractorOutputProtocol: class {
23 |
24 | func foundCities(cities: [City])
25 | }
26 |
27 | class AddInteractor {
28 |
29 | weak var presenter: AddInteractorOutputProtocol!
30 |
31 | var dataManager: AddDataManagerInputProtocol!
32 | }
33 |
34 | extension AddInteractor: AddInteractorInputProtocol {
35 |
36 | func getCitiesWithName(name: String) {
37 | self.dataManager.fetchCitiesWithName(name) { [weak self] (cities) -> () in
38 | self?.presenter.foundCities(cities)
39 | }
40 | }
41 |
42 | func saveCity(city: City) {
43 | self.dataManager.saveCityToPersistentStore(city)
44 | }
45 | }
46 |
47 | extension AddInteractor: AddDataManagerOutputProtocol {
48 |
49 | }
--------------------------------------------------------------------------------
/ViperWeather/CityTableViewCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AddTableViewCell.swift
3 | // ViperWeather
4 | //
5 | // Created by Dmitriy Utmanov on 02/03/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class CityTableViewCell: UITableViewCell {
12 |
13 | var city: City? {
14 | didSet {
15 | titleLabel.text = city?.title
16 | IDLabel.text = city?.ID
17 | placeIDLabel.text = city?.placeID
18 | }
19 | }
20 |
21 | @IBOutlet weak var titleLabel: UILabel!
22 | @IBOutlet weak var IDLabel: UILabel!
23 | @IBOutlet weak var placeIDLabel: UILabel!
24 |
25 | override func awakeFromNib() {
26 | super.awakeFromNib()
27 | // Initialization code
28 | let selectedBackgroundView = UIView(frame: CGRect.zero)
29 | selectedBackgroundView.backgroundColor = MaterialColor.lightBlueColor()
30 | self.selectedBackgroundView = selectedBackgroundView
31 | }
32 |
33 | override func setSelected(selected: Bool, animated: Bool) {
34 | super.setSelected(selected, animated: animated)
35 |
36 | // Configure the view for the selected state
37 | }
38 |
39 | override func prepareForReuse() {
40 | super.prepareForReuse()
41 |
42 | city = nil
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/ViperWeather/ChildAssembler.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ChildAssembler.swift
3 | // ViperWeather
4 | //
5 | // Created Dima on 16/03/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 | // Generated via Swift-Viper templates by https://github.com/cooler333
9 | //
10 |
11 | import UIKit
12 | import Swinject
13 |
14 |
15 | class ChildAssembler: Assembler {
16 |
17 | required init(parentAssembler: Assembler) {
18 | try! super.init(assemblies: [ChildContainer()], parentAssembler: parentAssembler)
19 | }
20 | }
21 |
22 | extension ChildAssembler {
23 |
24 | func presentChildViewController(fromViewController fromViewController: UIViewController) {
25 | let viewController = self.viewController()
26 | // setup viewController
27 |
28 | fromViewController.navigationController!.pushViewController(viewController, animated: true)
29 | }
30 |
31 | func storyboard() -> UIStoryboard {
32 | let storyboardName = "Detail"
33 | let bundle = NSBundle.mainBundle()
34 | return SwinjectStoryboard.create(name: storyboardName, bundle: bundle, container: resolver)
35 | }
36 |
37 | func viewController() -> ChildViewController {
38 | return storyboard().instantiateViewControllerWithIdentifier("ChildViewControllerID") as! ChildViewController
39 | }
40 | }
--------------------------------------------------------------------------------
/ViperWeather/RootPresenter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RootPresenter.swift
3 | // Architecture
4 | //
5 | // Created Dmitri Utmanov on 20/02/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 | // Generated by Swift-Viper templates. Find latest version at https://github.com/Nikita2k/SwiftViper
9 | //
10 |
11 | import UIKit
12 |
13 | protocol RootPresenterProtocol: class {
14 | func openListViewController()
15 | }
16 |
17 | protocol RootInterfaceProtocol: class {
18 |
19 | var presenter: RootPresenterProtocol! { get set }
20 |
21 | }
22 |
23 | class RootPresenter {
24 |
25 | weak private var interface: RootInterfaceProtocol?
26 | private let interactor: RootInteractorInputProtocol
27 | private let router: RootRouterInputProtocol
28 |
29 | init(interface: RootInterfaceProtocol, interactor: RootInteractorInputProtocol, router: RootRouterInputProtocol) {
30 | self.interface = interface
31 | self.interactor = interactor
32 | self.router = router
33 | }
34 |
35 | }
36 |
37 | extension RootPresenter: RootPresenterProtocol {
38 |
39 | func openListViewController() {
40 | self.router.openListViewController(fromViewController: interface as! UIViewController)
41 | }
42 |
43 | }
44 |
45 | extension RootPresenter: RootInteractorOutputProtocol {
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/ViperWeather/DetailListRouter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DetailListRouter.swift
3 | // ViperWeather
4 | //
5 | // Created Dmitri Utmanov on 03/03/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 | // Generated via Swift-Viper templates by https://github.com/cooler333
9 | //
10 |
11 | import UIKit
12 | import Swinject
13 |
14 |
15 | protocol DetailListRouterInputProtocol: class {
16 | func dismissDetailListViewController(viewController viewController: UIViewController)
17 | func presentDetailListDetailViewController(fromViewController fromViewController: UIViewController)
18 |
19 | var detailListDetailAssembler: DetailListDetailAssembler! { get set }
20 | }
21 |
22 | protocol DetailListParentRouterProtocol: class {
23 |
24 | }
25 |
26 | class DetailListRouter: DetailListRouterInputProtocol {
27 |
28 | var detailListDetailAssembler: DetailListDetailAssembler!
29 |
30 |
31 | func dismissDetailListViewController(viewController viewController: UIViewController) {
32 | viewController.dismissViewControllerAnimated(true, completion: nil)
33 | }
34 |
35 | func presentDetailListDetailViewController(fromViewController fromViewController: UIViewController) {
36 | detailListDetailAssembler.presentDetailListDetailViewController(fromViewController: fromViewController)
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/ViperWeather/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | #Viper App Template inspired by Rambler
3 |
4 | ## Features
5 |
6 | - [x] Swinject - Dependency Injection
7 | - [x] RealmSwift - Persistent Storage
8 | - [x] SwiftFetchedResultsController (NSFetchedResultsController for Realm written in Swift)
9 | - [x] Alamofire - Networking
10 | - [x] UIStoryboard + XIB
11 | - [x] UIAppearance
12 | - [ ] MaterialColor -> CocoaPods
13 | - [ ] SnapKit
14 |
15 |
16 | ## How to use
17 |
18 | 1. Go to: https://console.developers.google.com/apis/credentials
19 | 2. Sign In
20 | 3. Registrate Server API key
21 | 4. Paste key to Constants.swift -> googleMapKey
22 | 5. Go to https://home.openweathermap.org/ or http://openweathermap.org/api
23 | 6. Sign In
24 | 7. Paste key to Constants.swift -> openWeatherMapKey
25 | 8. Install CocoaPods
26 | 9. Go to application folder
27 | 10. Run "pod install" in terminal
28 | 11. Run application and enjoy!
29 |
30 | # Feedback
31 | ### I've found a bug, or have a feature request
32 |
33 | Please raise a GitHub issue.
34 |
35 | ### I'm blown away!
36 |
37 | ViperWeather is a non-profit, community driven project. We only ask that if you've found it useful to star us on Github. If you've written a related blog or tutorial, or published a new app based on ViperWeather codebase, we'd certainly be happy to hear about that too.
38 |
--------------------------------------------------------------------------------
/ViperWeather/DetailListDetailAssembler.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DetailListDetailAssembler.swift
3 | // ViperWeather
4 | //
5 | // Created Dima on 16/03/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 | // Generated via Swift-Viper templates by https://github.com/cooler333
9 | //
10 |
11 | import UIKit
12 | import Swinject
13 |
14 |
15 | class DetailListDetailAssembler: Assembler {
16 |
17 | required init(parentAssembler: Assembler) {
18 | try! super.init(assemblies: [DetailListDetailContainer()], parentAssembler: parentAssembler)
19 | }
20 | }
21 |
22 | extension DetailListDetailAssembler {
23 |
24 | func presentDetailListDetailViewController(fromViewController fromViewController: UIViewController) {
25 | let viewController = self.viewController()
26 | // setup viewController
27 |
28 | fromViewController.navigationController!.pushViewController(viewController, animated: true)
29 | }
30 |
31 | func storyboard() -> UIStoryboard {
32 | let storyboardName = "Detail"
33 | let bundle = NSBundle.mainBundle()
34 | return SwinjectStoryboard.create(name: storyboardName, bundle: bundle, container: resolver)
35 | }
36 |
37 | func viewController() -> DetailListDetailViewController {
38 | return storyboard().instantiateViewControllerWithIdentifier("DetailListDetailViewControllerID") as! DetailListDetailViewController
39 | }
40 | }
--------------------------------------------------------------------------------
/ViperWeather/WeatherTableViewCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WeatherTableViewCell.swift
3 | // ViperWeather
4 | //
5 | // Created by Dmitri Utmanov on 03/03/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class WeatherTableViewCell: UITableViewCell {
12 |
13 | var weather: Weather? {
14 | didSet {
15 | if let weather = weather {
16 | let dateFormatter = NSDateFormatter()
17 | dateFormatter.dateStyle = .MediumStyle
18 | dateFormatter.timeStyle = .MediumStyle
19 | dateLabel.text = dateFormatter.stringFromDate(NSDate(timeIntervalSince1970: weather.dt))
20 |
21 | weatherLabel.text = weather.tempString
22 | } else {
23 | dateLabel.text = nil
24 | weatherLabel.text = nil
25 | }
26 | }
27 | }
28 |
29 | @IBOutlet weak var dateLabel: UILabel!
30 | @IBOutlet weak var weatherLabel: UILabel!
31 |
32 |
33 | override func awakeFromNib() {
34 | super.awakeFromNib()
35 | // Initialization code
36 | }
37 |
38 | override func setSelected(selected: Bool, animated: Bool) {
39 | super.setSelected(selected, animated: animated)
40 |
41 | // Configure the view for the selected state
42 | }
43 |
44 | override func prepareForReuse() {
45 | super.prepareForReuse()
46 |
47 | weather = nil
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/ViperWeather/DetailInteractor.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DetailInteractor.swift
3 | // ViperWeather
4 | //
5 | // Created by Dmitriy Utmanov on 29/02/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 |
12 | protocol DetailInteractorInputProtocol: class {
13 |
14 | weak var presenter: DetailInteractorOutputProtocol! { get set }
15 |
16 | func getDetailCity(city: City)
17 | func getWeatherForCity(city: City)
18 | }
19 |
20 | protocol DetailInteractorOutputProtocol: class {
21 |
22 | func foundDetailCity(city: City)
23 | func foundWeatherForCity(weather: [Weather], city: City)
24 | }
25 |
26 | class DetailInteractor {
27 |
28 | weak var presenter: DetailInteractorOutputProtocol!
29 |
30 | var dataManager: DetailDataManagerInputProtocol!
31 |
32 | }
33 |
34 | extension DetailInteractor: DetailInteractorInputProtocol {
35 |
36 | func getDetailCity(city: City) {
37 | self.dataManager.getDetailCity(city) { [weak self] (city) -> () in
38 | self?.dataManager.updateCityInPersistentStore(city)
39 | self?.presenter.foundDetailCity(city)
40 | }
41 | }
42 |
43 | func getWeatherForCity(city: City) {
44 | self.dataManager.getWeatherForCity(city) { [weak self] (weather) -> () in
45 | self?.presenter.foundWeatherForCity(weather, city: city)
46 | }
47 | }
48 |
49 | }
50 |
51 | extension DetailInteractor: DetailDataManagerOutputProtocol {
52 |
53 | }
--------------------------------------------------------------------------------
/ViperWeather/DetailListDetailPresenter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DetailListDetailPresenter.swift
3 | // ViperWeather
4 | //
5 | // Created Dima on 16/03/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 | // Generated via Swift-Viper templates by https://github.com/cooler333
9 | //
10 |
11 | import UIKit
12 | import Swinject
13 |
14 |
15 | protocol DetailListDetailPresenterProtocol: class {
16 | func presentChildViewController()
17 | }
18 |
19 | protocol DetailListDetailInterfaceProtocol: class {
20 |
21 | var presenter: DetailListDetailPresenterProtocol! { get set }
22 | }
23 |
24 | class DetailListDetailPresenter {
25 |
26 | weak private var interface: DetailListDetailInterfaceProtocol!
27 | private let interactor: DetailListDetailInteractorInputProtocol
28 | private let router: DetailListDetailRouterInputProtocol
29 |
30 |
31 | init(interface: DetailListDetailInterfaceProtocol, interactor: DetailListDetailInteractorInputProtocol, router: DetailListDetailRouterInputProtocol) {
32 | self.interface = interface
33 | self.interactor = interactor
34 | self.router = router
35 | }
36 |
37 | }
38 |
39 |
40 | extension DetailListDetailPresenter: DetailListDetailPresenterProtocol {
41 |
42 | func presentChildViewController() {
43 | self.router.presentChildViewController(fromViewController: self.interface as! UIViewController)
44 | }
45 | }
46 |
47 | extension DetailListDetailPresenter: DetailListDetailInteractorOutputProtocol {
48 |
49 | }
50 |
51 |
--------------------------------------------------------------------------------
/ViperWeather/DetailListDetailViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DetailListDetailViewController.swift
3 | // ViperWeather
4 | //
5 | // Created Dima on 16/03/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 | // Generated via Swift-Viper templates by https://github.com/cooler333
9 | //
10 |
11 | import UIKit
12 |
13 |
14 | class DetailListDetailViewController: UIViewController {
15 |
16 | // MARK: - VIPER Properties
17 | var presenter: DetailListDetailPresenterProtocol!
18 |
19 |
20 | override func viewDidLoad() {
21 | super.viewDidLoad()
22 | // Do any additional setup after loading the view.
23 | }
24 |
25 | override func viewWillAppear(animated: Bool) {
26 | super.viewWillAppear(animated)
27 | // Update data.
28 | }
29 |
30 |
31 | // MARK: ChildViewController Presentation Action
32 |
33 | // Uncomment this snippet for present new ViewController after current ViewController called viewDidAppear func
34 | // or paste line to another func (e.g. tap on UIBitton -> IBAction)
35 | override func viewDidAppear(animated: Bool) {
36 | super.viewDidAppear(animated)
37 |
38 | self.presenter.presentChildViewController()
39 | }
40 |
41 |
42 | override func didReceiveMemoryWarning() {
43 | super.didReceiveMemoryWarning()
44 | // Dispose of any resources that can be recreated.
45 | }
46 |
47 | }
48 |
49 | extension DetailListDetailViewController: DetailListDetailInterfaceProtocol {
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/ViperWeather/RootViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RootViewController.swift
3 | // Architecture
4 | //
5 | // Created Dmitri Utmanov on 20/02/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 | // Generated by Swift-Viper templates. Find latest version at https://github.com/Nikita2k/SwiftViper
9 | //
10 |
11 | import UIKit
12 |
13 |
14 | class RootViewController: UIViewController {
15 |
16 | var presenter: RootPresenterProtocol!
17 |
18 | @IBOutlet weak var activityIndicatorView: UIActivityIndicatorView!
19 |
20 | override var nibName: String? {
21 | get {
22 | let classString = String(self.dynamicType)
23 | return classString
24 | }
25 | }
26 | override var nibBundle: NSBundle? {
27 | get {
28 | return NSBundle.mainBundle()
29 | }
30 | }
31 |
32 | override func viewDidLoad() {
33 | super.viewDidLoad()
34 |
35 | self.navigationController!.setNavigationBarHidden(true, animated: false)
36 |
37 | activityIndicatorView.color = MaterialColor.cyanColor()
38 | }
39 |
40 | override func viewDidAppear(animated: Bool) {
41 | super.viewDidAppear(animated)
42 |
43 | // Imitation of login request or fetch some data
44 | let time = dispatch_time(DISPATCH_TIME_NOW, Int64(1.0 * Double(NSEC_PER_SEC)))
45 | dispatch_after(time, dispatch_get_main_queue()) { [weak self] () -> Void in
46 | self?.presenter.openListViewController()
47 | }
48 | }
49 | }
50 |
51 | extension RootViewController: RootInterfaceProtocol {
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/ViperWeather/DetailListInteractor.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DetailListInteractor.swift
3 | // ViperWeather
4 | //
5 | // Created Dmitri Utmanov on 03/03/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 | // Generated via Swift-Viper templates by https://github.com/cooler333
9 | //
10 |
11 | import Foundation
12 |
13 |
14 | protocol DetailListInteractorInputProtocol: class {
15 |
16 | weak var presenter: DetailListInteractorOutputProtocol! { get set }
17 |
18 | func getDetailCity(city: City)
19 | func getWeatherForCity(city: City)
20 | }
21 |
22 | protocol DetailListInteractorOutputProtocol: class {
23 |
24 | func foundDetailCity(city: City)
25 | func foundWeatherForCity(weather: [Weather], city: City)
26 | }
27 |
28 | class DetailListInteractor {
29 |
30 | weak var presenter: DetailListInteractorOutputProtocol!
31 |
32 | var dataManager: DetailListDataManagerInputProtocol!
33 |
34 | }
35 |
36 | extension DetailListInteractor: DetailListInteractorInputProtocol {
37 |
38 | func getDetailCity(city: City) {
39 | self.dataManager.getDetailCity(city) { [weak self] (city) -> () in
40 | self?.dataManager.updateCityInPersistentStore(city)
41 | self?.presenter.foundDetailCity(city)
42 | }
43 | }
44 |
45 | func getWeatherForCity(city: City) {
46 | self.dataManager.getWeatherForCity(city) { [weak self] (weather) -> () in
47 | self?.presenter.foundWeatherForCity(weather, city: city)
48 | }
49 | }
50 | }
51 |
52 | extension DetailListInteractor: DetailListDataManagerOutputProtocol {
53 |
54 | }
--------------------------------------------------------------------------------
/ViperWeather/ListRouter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ListRouter.swift
3 | // ViperWeather
4 | //
5 | // Created by Dmitriy Utmanov on 29/02/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Swinject
11 |
12 |
13 | protocol ListRouterInputProtocol: class {
14 | func presentAddViewController(fromViewController fromViewController: UIViewController)
15 | func presentDetailViewController(fromViewController fromViewController: UIViewController, city: City)
16 |
17 | func dismissListViewController(viewController viewController: UIViewController)
18 |
19 | var detailContainerAssembler: DetailContainerAssembler! { get set }
20 | var addAssembler: AddAssembler! { get set }
21 | }
22 |
23 | protocol ListParentRouterProtocol: class {
24 |
25 | }
26 |
27 | class ListRouter: ListRouterInputProtocol {
28 |
29 | var detailContainerAssembler: DetailContainerAssembler!
30 | var addAssembler: AddAssembler!
31 |
32 |
33 | func dismissListViewController(viewController viewController: UIViewController) {
34 | viewController.dismissViewControllerAnimated(true, completion: nil)
35 | }
36 |
37 | func presentAddViewController(fromViewController fromViewController: UIViewController) {
38 | addAssembler.presentAddViewController(fromViewController: fromViewController)
39 | }
40 |
41 | func presentDetailViewController(fromViewController fromViewController: UIViewController, city: City) {
42 | detailContainerAssembler.presentDetailContainerViewController(fromViewController: fromViewController, city: city)
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## Build generated
6 | build/
7 | DerivedData
8 |
9 | ## Various settings
10 | *.pbxuser
11 | !default.pbxuser
12 | *.mode1v3
13 | !default.mode1v3
14 | *.mode2v3
15 | !default.mode2v3
16 | *.perspectivev3
17 | !default.perspectivev3
18 | xcuserdata
19 |
20 | ## Other
21 | *.xccheckout
22 | *.moved-aside
23 | *.xcuserstate
24 | *.xcscmblueprint
25 |
26 | ## Obj-C/Swift specific
27 | *.hmap
28 | *.ipa
29 |
30 | ## Playgrounds
31 | timeline.xctimeline
32 | playground.xcworkspace
33 |
34 | # Swift Package Manager
35 | #
36 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
37 | # Packages/
38 | .build/
39 |
40 | # CocoaPods
41 | #
42 | # We recommend against adding the Pods directory to your .gitignore. However
43 | # you should judge for yourself, the pros and cons are mentioned at:
44 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
45 | #
46 | Pods/
47 |
48 | # Carthage
49 | #
50 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
51 | # Carthage/Checkouts
52 |
53 | Carthage/Build
54 |
55 | # fastlane
56 | #
57 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
58 | # screenshots whenever they are needed.
59 | # For more information about the recommended setup visit:
60 | # https://github.com/fastlane/fastlane/blob/master/docs/Gitignore.md
61 |
62 | fastlane/report.xml
63 | fastlane/screenshots
64 |
--------------------------------------------------------------------------------
/ViperWeather/AddAssembler.swift:
--------------------------------------------------------------------------------
1 |
2 | // AddAssembler.swift
3 | // Architecture
4 | //
5 | // Created Dmitri Utmanov on 19/02/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 | // Generated by Swift-Viper templates. Find latest version at https://github.com/Nikita2k/SwiftViper
9 | //
10 |
11 | import UIKit
12 | import Swinject
13 |
14 | class AddAssembler: Assembler {
15 |
16 | init(parentAssembler: Assembler) {
17 | try! super.init(assemblies: [AddContainer()], parentAssembler: parentAssembler)
18 | }
19 | }
20 |
21 | extension AddAssembler {
22 |
23 | func presentAddViewController(fromViewController fromViewController: UIViewController) {
24 | let viewController = storyboard().instantiateViewControllerWithIdentifier("AddTableViewControllerID") as! AddTableViewController
25 |
26 | viewController.presenter.delegate = fromViewController as? AddViewControllerDelegate
27 |
28 | let idiom = UIDevice.currentDevice().userInterfaceIdiom
29 | switch idiom {
30 | case .Phone:
31 | let navigationController = UINavigationController(rootViewController: viewController)
32 | fromViewController.presentViewController(navigationController, animated: true, completion: nil)
33 |
34 | case .Pad:
35 | fromViewController.navigationController!.pushViewController(viewController, animated: true)
36 |
37 | default:
38 | fatalError("Device is not supported yet")
39 | }
40 | }
41 |
42 | func storyboard() -> SwinjectStoryboard {
43 | return SwinjectStoryboard.create(name: "List", bundle: NSBundle.mainBundle(), container: resolver)
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/ViperWeather/ListPresenter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ListPresenter.swift
3 | // ViperWeather
4 | //
5 | // Created by Dmitriy Utmanov on 29/02/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Swinject
11 |
12 |
13 | protocol ListPresenterProtocol: class {
14 |
15 | func showDetailCity(city: City)
16 | func addNew()
17 | func exit()
18 | func removeCity(city: City)
19 | }
20 |
21 | protocol ListInterfaceProtocol: class {
22 |
23 | var presenter: ListPresenterProtocol! { get set }
24 | }
25 |
26 | class ListPresenter {
27 |
28 | weak private var interface: ListInterfaceProtocol!
29 | private let interactor: ListInteractorInputProtocol
30 | private let router: ListRouterInputProtocol
31 |
32 |
33 | init(interface: ListInterfaceProtocol, interactor: ListInteractorInputProtocol, router: ListRouterInputProtocol) {
34 | self.interface = interface
35 | self.interactor = interactor
36 | self.router = router
37 | }
38 |
39 | }
40 |
41 |
42 | extension ListPresenter: ListPresenterProtocol {
43 |
44 | func showDetailCity(city: City) {
45 | self.router.presentDetailViewController(fromViewController: self.interface as! UIViewController, city: city)
46 | }
47 |
48 | func removeCity(city: City) {
49 | self.interactor.removeCity(city)
50 | }
51 |
52 | func addNew() {
53 | self.router.presentAddViewController(fromViewController: self.interface as! UIViewController)
54 | }
55 |
56 | func exit() {
57 | self.router.dismissListViewController(viewController: self.interface as! UIViewController)
58 | }
59 | }
60 |
61 | extension ListPresenter: ListInteractorOutputProtocol {
62 |
63 | }
64 |
65 |
--------------------------------------------------------------------------------
/ViperWeather/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/ViperWeather/ListDataManager.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ListDataManager.swift
3 | // ViperWeather
4 | //
5 | // Created by Dmitri Utmanov on 02/03/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import RealmSwift
11 | import Alamofire
12 |
13 |
14 | protocol ListDataManagerInputProtocol: class {
15 |
16 | weak var interactor: ListDataManagerOutputProtocol! { get set }
17 |
18 | func fetchCitiesFromPersistentStore(callback: ([City]) -> ())
19 | func removeCityFromPersistentStore(city: City)
20 | }
21 |
22 | protocol ListDataManagerOutputProtocol: class {
23 |
24 | var dataManager: ListDataManagerInputProtocol! { get set }
25 | }
26 |
27 |
28 | class ListDataManager {
29 |
30 | weak var interactor: ListDataManagerOutputProtocol!
31 | }
32 |
33 | extension ListDataManager: ListDataManagerInputProtocol {
34 |
35 | func fetchCitiesFromPersistentStore(callback: ([City]) -> ()) {
36 | let realm = try! Realm()
37 |
38 | let cityEntities = realm.objects(CityEntity)
39 |
40 | var cities: [City] = []
41 | for cityEntity in cityEntities {
42 | let city = City(title: cityEntity.title, ID: cityEntity.ID, placeID: cityEntity.placeID, lat: cityEntity.lat, lng: cityEntity.lng)
43 | cities.append(city)
44 | }
45 | callback(cities)
46 | }
47 |
48 | func removeCityFromPersistentStore(city: City) {
49 | let realm = try! Realm()
50 |
51 | realm.beginWrite()
52 |
53 | let predicate = NSPredicate(format: "ID = %@", argumentArray: [city.ID])
54 | let cityEntities = realm.objects(CityEntity).filter(predicate)
55 |
56 | realm.deleteWithNotification(cityEntities)
57 |
58 | try! realm.commitWrite()
59 | }
60 |
61 | }
--------------------------------------------------------------------------------
/ViperWeather/RootContainer.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RootContainer.swift
3 | // Architecture
4 | //
5 | // Created Dmitri Utmanov on 20/02/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 | // Generated by Swift-Viper templates. Find latest version at https://github.com/Nikita2k/SwiftViper
9 | //
10 |
11 | import UIKit
12 | import Swinject
13 |
14 | class RootContainer: AssemblyType {
15 |
16 | func assemble(container: Container) {
17 | container.registerForStoryboard(RootViewController.self) { (r, c) -> () in
18 | container.register(RootPresenterProtocol.self) { [weak c] r in
19 | guard let c = c else { fatalError("Contoller is nil") }
20 |
21 | let interface = c
22 | let interactor = r.resolve(RootInteractorInputProtocol.self)!
23 | let router = r.resolve(RootRouterInputProtocol.self)!
24 |
25 | let presenter = RootPresenter(interface: interface, interactor: interactor, router: router)
26 | interactor.presenter = presenter
27 |
28 | return presenter
29 | }
30 | c.presenter = r.resolve(RootPresenterProtocol.self)
31 | }
32 |
33 | container.register(RootInteractorInputProtocol.self) { r in
34 | return RootInteractor()
35 | }
36 |
37 | container.register(RootRouterInputProtocol.self) { r in
38 | let router = RootRouter()
39 | router.listAssembler = r.resolve(ListAssembler.self)!
40 | return router
41 | }
42 |
43 | container.register(ListAssembler.self) { r in
44 | let parentAssembler = r.resolve(RootAssembler.self)!
45 | return ListAssembler(parentAssembler: parentAssembler)
46 | }
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/ViperWeather/DetailContainerRouter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DetailContainerRouter.swift
3 | // ViperWeather
4 | //
5 | // Created Dmitri Utmanov on 03/03/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 | // Generated via Swift-Viper templates by https://github.com/cooler333
9 | //
10 |
11 | import UIKit
12 | import Swinject
13 |
14 |
15 | protocol DetailContainerRouterInputProtocol: class {
16 | func dismissDetailContainerViewController(viewController viewController: UIViewController)
17 |
18 | func presentDetailViewController(viewController viewController: UIViewController, view: UIView, city: City)
19 | func presentDetailListViewController(viewController viewController: UIViewController, view: UIView, city: City)
20 |
21 | var detailAssembler: DetailAssembler! { get set }
22 | var detailListAssembler: DetailListAssembler! { get set }
23 | }
24 |
25 | protocol DetailContainerParentRouterProtocol: class {
26 |
27 | }
28 |
29 | class DetailContainerRouter: DetailContainerRouterInputProtocol {
30 |
31 | var detailAssembler: DetailAssembler!
32 | var detailListAssembler: DetailListAssembler!
33 |
34 |
35 | func dismissDetailContainerViewController(viewController viewController: UIViewController) {
36 | viewController.dismissViewControllerAnimated(true, completion: nil)
37 | }
38 |
39 | func presentDetailViewController(viewController viewController: UIViewController, view: UIView, city: City) {
40 | detailAssembler.presentDetailViewController(fromViewController: viewController, inView: view, city: city)
41 | }
42 |
43 | func presentDetailListViewController(viewController viewController: UIViewController, view: UIView, city: City) {
44 | detailListAssembler.presentDetailListViewController(fromViewController: viewController, inView: view, city: city)
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/ViperWeather/DetailContainer.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DetailContainer.swift
3 | // ViperWeather
4 | //
5 | // Created by Dmitriy Utmanov on 29/02/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Swinject
11 |
12 |
13 | class DetailContainer: AssemblyType {
14 |
15 | func assemble(container: Container) {
16 | container.registerForStoryboard(DetailViewController.self) { (r, c) -> () in
17 | container.register(DetailPresenterProtocol.self) { [weak c] r in
18 | guard let c = c else { fatalError("Contoller is nil") }
19 |
20 | let interface = c
21 | let interactor = r.resolve(DetailInteractorInputProtocol.self)!
22 | let router = r.resolve(DetailRouterInputProtocol.self)!
23 |
24 | let presenter = DetailPresenter(interface: interface, interactor: interactor, router: router)
25 | interactor.presenter = presenter
26 |
27 | return presenter
28 | }
29 | c.presenter = r.resolve(DetailPresenterProtocol.self)
30 | }
31 |
32 | container.register(DetailInteractorInputProtocol.self) { r in
33 | let interactor = DetailInteractor()
34 | let dataManager = r.resolve(DetailDataManagerInputProtocol.self)!
35 | interactor.dataManager = dataManager
36 | dataManager.interactor = interactor
37 | return interactor
38 | }
39 |
40 | container.register(DetailRouterInputProtocol.self) { (r) in
41 | let router = DetailRouter()
42 | return router
43 | }
44 |
45 | container.register(DetailDataManagerInputProtocol.self) { (r) in
46 | let dataManager = DetailDataManager()
47 | return dataManager
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/ViperWeather/DetailContainerAssembler.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DetailContainerAssembler.swift
3 | // ViperWeather
4 | //
5 | // Created Dmitri Utmanov on 03/03/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 | // Generated via Swift-Viper templates by https://github.com/cooler333
9 | //
10 |
11 | import UIKit
12 | import Swinject
13 |
14 |
15 | class DetailContainerAssembler: Assembler {
16 |
17 | required init(parentAssembler: Assembler) {
18 | try! super.init(assemblies: [DetailContainerContainer()], parentAssembler: parentAssembler)
19 | }
20 | }
21 |
22 | extension DetailContainerAssembler {
23 |
24 | func presentDetailContainerViewController(fromViewController fromViewController: UIViewController, city: City) {
25 | let viewController = self.viewController()
26 | viewController.city = city
27 |
28 | let idiom = UIDevice.currentDevice().userInterfaceIdiom
29 | switch idiom {
30 | case .Phone:
31 | fromViewController.navigationController!.pushViewController(viewController, animated: true)
32 |
33 | case .Pad:
34 | let navigationController = fromViewController.splitViewController?.viewControllers[1] as! UINavigationController
35 | navigationController.setViewControllers([viewController], animated: false)
36 |
37 | default:
38 | fatalError("Device is not supported yet")
39 | }
40 |
41 | }
42 |
43 | func storyboard() -> UIStoryboard {
44 | let storyboardName = "Detail"
45 | let bundle = NSBundle.mainBundle()
46 | return SwinjectStoryboard.create(name: storyboardName, bundle: bundle, container: resolver)
47 | }
48 |
49 | func viewController() -> DetailContainerViewController {
50 | return storyboard().instantiateInitialViewController() as! DetailContainerViewController
51 | }
52 | }
--------------------------------------------------------------------------------
/ViperWeather/DetailContainerPresenter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DetailContainerPresenter.swift
3 | // ViperWeather
4 | //
5 | // Created Dmitri Utmanov on 03/03/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 | // Generated via Swift-Viper templates by https://github.com/cooler333
9 | //
10 |
11 | import UIKit
12 | import Swinject
13 |
14 |
15 | protocol DetailContainerPresenterProtocol: class {
16 |
17 | func presentDetailViewControllerInView(view: UIView, city: City)
18 | func presentDetailListViewControllerInView(view: UIView, city: City)
19 | }
20 |
21 | protocol DetailContainerInterfaceProtocol: class {
22 |
23 | var presenter: DetailContainerPresenterProtocol! { get set }
24 | }
25 |
26 | class DetailContainerPresenter {
27 |
28 | weak private var interface: DetailContainerInterfaceProtocol!
29 | private let interactor: DetailContainerInteractorInputProtocol
30 | private let router: DetailContainerRouterInputProtocol
31 |
32 |
33 | init(interface: DetailContainerInterfaceProtocol, interactor: DetailContainerInteractorInputProtocol, router: DetailContainerRouterInputProtocol) {
34 | self.interface = interface
35 | self.interactor = interactor
36 | self.router = router
37 | }
38 | }
39 |
40 |
41 | extension DetailContainerPresenter: DetailContainerPresenterProtocol {
42 |
43 | func presentDetailViewControllerInView(view: UIView, city: City) {
44 | self.router.presentDetailViewController(viewController: self.interface as! UIViewController, view: view, city: city)
45 | }
46 |
47 | func presentDetailListViewControllerInView(view: UIView, city: City) {
48 | self.router.presentDetailListViewController(viewController: self.interface as! UIViewController, view: view, city: city)
49 | }
50 | }
51 |
52 | extension DetailContainerPresenter: DetailContainerInteractorOutputProtocol {
53 |
54 | }
55 |
56 |
--------------------------------------------------------------------------------
/ViperWeather/DetailPresenter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DetailPresenter.swift
3 | // ViperWeather
4 | //
5 | // Created by Dmitriy Utmanov on 29/02/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Swinject
11 |
12 |
13 | protocol DetailPresenterProtocol: class {
14 |
15 | func getDetailCity(city: City)
16 | func getWeatherForCity(city: City)
17 | }
18 |
19 | protocol DetailInterfaceProtocol: class {
20 |
21 | var presenter: DetailPresenterProtocol! { get set }
22 |
23 | func showEmpty()
24 | func showCity(city: City)
25 | func showWeatherForCity(weather: [Weather], city: City)
26 | }
27 |
28 | class DetailPresenter {
29 |
30 | weak private var interface: DetailInterfaceProtocol!
31 | private let interactor: DetailInteractorInputProtocol
32 | private let router: DetailRouterInputProtocol
33 |
34 |
35 | init(interface: DetailInterfaceProtocol, interactor: DetailInteractorInputProtocol, router: DetailRouterInputProtocol) {
36 | self.interface = interface
37 | self.interactor = interactor
38 | self.router = router
39 | }
40 | }
41 |
42 |
43 | extension DetailPresenter: DetailPresenterProtocol {
44 |
45 | func getDetailCity(city: City) {
46 | self.interactor.getDetailCity(city)
47 | }
48 |
49 | func getWeatherForCity(city: City) {
50 | self.interactor.getWeatherForCity(city)
51 | }
52 | }
53 |
54 | extension DetailPresenter: DetailInteractorOutputProtocol {
55 |
56 | func foundDetailCity(city: City) {
57 | guard city.isLocationEnable() == true else {
58 | self.interface.showEmpty()
59 | return
60 | }
61 | self.interface.showCity(city)
62 | }
63 |
64 | func foundWeatherForCity(weather: [Weather], city: City) {
65 | self.interface.showWeatherForCity(weather, city: city)
66 | }
67 | }
68 |
69 |
--------------------------------------------------------------------------------
/ViperWeather/ListAssembler.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ListAssembler.swift
3 | // ViperWeather
4 | //
5 | // Created by Dmitriy Utmanov on 29/02/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Swinject
11 |
12 |
13 | class ListAssembler: Assembler {
14 |
15 | required init(parentAssembler: Assembler) {
16 | try! super.init(assemblies: [ListContainer()], parentAssembler: parentAssembler)
17 | }
18 | }
19 |
20 | extension ListAssembler {
21 |
22 | func presentListViewController(fromViewController fromViewController: UIViewController) {
23 | let viewController = storyboard().instantiateViewControllerWithIdentifier("ListTableViewControllerID") as! ListTableViewController
24 |
25 | let navigationController = UINavigationController(rootViewController: viewController)
26 |
27 | let idiom = UIDevice.currentDevice().userInterfaceIdiom
28 | switch idiom {
29 | case .Phone:
30 | fromViewController.presentViewController(navigationController, animated: true, completion: nil)
31 |
32 | case .Pad:
33 | let splitViewController = UISplitViewController()
34 | let detailNavigationController = UINavigationController()
35 | splitViewController.viewControllers = [navigationController, detailNavigationController]
36 |
37 | splitViewController.presentsWithGesture = false
38 | splitViewController.preferredDisplayMode = .AllVisible
39 |
40 | fromViewController.presentViewController(splitViewController, animated: true, completion: nil)
41 |
42 | default:
43 | fatalError("Device is not supported yet")
44 | }
45 | }
46 |
47 | func storyboard() -> UIStoryboard {
48 | return SwinjectStoryboard.create(name: "List", bundle: NSBundle.mainBundle(), container: resolver)
49 | }
50 |
51 | }
--------------------------------------------------------------------------------
/ViperWeather/ChildContainer.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ChildContainer.swift
3 | // ViperWeather
4 | //
5 | // Created Dima on 16/03/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 | // Generated via Swift-Viper templates by https://github.com/cooler333
9 | //
10 |
11 | import UIKit
12 | import Swinject
13 |
14 |
15 | class ChildContainer: AssemblyType {
16 |
17 | func assemble(container: Container) {
18 | container.registerForStoryboard(ChildViewController.self) { (r, c) -> () in
19 | container.register(ChildPresenterProtocol.self) { [weak c] r in
20 | guard let c = c else { fatalError("ViewController is nil") }
21 |
22 | let interface = c
23 | let interactor = r.resolve(ChildInteractorInputProtocol.self)!
24 | let router = r.resolve(ChildRouterInputProtocol.self)!
25 |
26 | let presenter = ChildPresenter(interface: interface, interactor: interactor, router: router)
27 | interactor.presenter = presenter
28 |
29 | return presenter
30 | }
31 | c.presenter = r.resolve(ChildPresenterProtocol.self)
32 | }
33 |
34 | container.register(ChildInteractorInputProtocol.self) { r in
35 | let interactor = ChildInteractor()
36 | let dataManager = r.resolve(ChildDataManagerInputProtocol.self)!
37 | interactor.dataManager = dataManager
38 | dataManager.interactor = interactor
39 | return interactor
40 | }
41 |
42 | container.register(ChildRouterInputProtocol.self) { (r) in
43 | let router = ChildRouter()
44 | return router
45 | }
46 |
47 | container.register(ChildDataManagerInputProtocol.self) { (r) in
48 | let dataManager = ChildDataManager()
49 | return dataManager
50 | }
51 |
52 |
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/ViperWeather/AddContainer.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AddContainer.swift
3 | // Architecture
4 | //
5 | // Created Dmitri Utmanov on 19/02/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 | // Generated by Swift-Viper templates. Find latest version at https://github.com/Nikita2k/SwiftViper
9 | //
10 |
11 | import UIKit
12 | import Swinject
13 |
14 | class AddContainer: AssemblyType {
15 |
16 | func assemble(container: Container) {
17 | container.registerForStoryboard(AddTableViewController.self) { (r, c) -> () in
18 | container.register(AddPresenterProtocol.self) { [weak c] r in
19 | guard let c = c else { fatalError("Contoller is nil") }
20 |
21 | let interface = c
22 | let interactor = r.resolve(AddInteractorInputProtocol.self)!
23 | let router = r.resolve(AddRouterInputProtocol.self)!
24 |
25 | let presenter = AddPresenter(interface: interface, interactor: interactor, router: router)
26 | interactor.presenter = presenter
27 |
28 | return presenter
29 | }
30 | c.presenter = r.resolve(AddPresenterProtocol.self)
31 | }
32 |
33 | container.register(AddInteractorInputProtocol.self) { r in
34 | let interactor = AddInteractor()
35 | let dataManager = r.resolve(AddDataManagerInputProtocol.self)!
36 | interactor.dataManager = dataManager
37 | dataManager.interactor = interactor
38 | return interactor
39 | }
40 |
41 | container.register(AddRouterInputProtocol.self) { (r) in
42 | let router = AddRouter()
43 | return router
44 | }
45 |
46 | container.register(AddDataManagerInputProtocol.self) { (r) in
47 | let dataManager = AddDataManager()
48 | return dataManager
49 | }
50 | }
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/ViperWeather/ListTableViewController.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/ViperWeather/AddTableViewController.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/ViperWeather/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "29x29",
5 | "idiom" : "iphone",
6 | "filename" : "Icon-Small@2x.png",
7 | "scale" : "2x"
8 | },
9 | {
10 | "size" : "29x29",
11 | "idiom" : "iphone",
12 | "filename" : "Icon-Small@3x.png",
13 | "scale" : "3x"
14 | },
15 | {
16 | "size" : "40x40",
17 | "idiom" : "iphone",
18 | "filename" : "Icon-Small-40@2x.png",
19 | "scale" : "2x"
20 | },
21 | {
22 | "size" : "40x40",
23 | "idiom" : "iphone",
24 | "filename" : "Icon-Small-40@3x.png",
25 | "scale" : "3x"
26 | },
27 | {
28 | "size" : "60x60",
29 | "idiom" : "iphone",
30 | "filename" : "Icon-60@2x.png",
31 | "scale" : "2x"
32 | },
33 | {
34 | "size" : "60x60",
35 | "idiom" : "iphone",
36 | "filename" : "Icon-60@3x.png",
37 | "scale" : "3x"
38 | },
39 | {
40 | "size" : "29x29",
41 | "idiom" : "ipad",
42 | "filename" : "Icon-Small.png",
43 | "scale" : "1x"
44 | },
45 | {
46 | "size" : "29x29",
47 | "idiom" : "ipad",
48 | "filename" : "Icon-Small@2x-1.png",
49 | "scale" : "2x"
50 | },
51 | {
52 | "size" : "40x40",
53 | "idiom" : "ipad",
54 | "filename" : "Icon-Small-40.png",
55 | "scale" : "1x"
56 | },
57 | {
58 | "size" : "40x40",
59 | "idiom" : "ipad",
60 | "filename" : "Icon-Small-40@2x-1.png",
61 | "scale" : "2x"
62 | },
63 | {
64 | "size" : "76x76",
65 | "idiom" : "ipad",
66 | "filename" : "Icon-76.png",
67 | "scale" : "1x"
68 | },
69 | {
70 | "size" : "76x76",
71 | "idiom" : "ipad",
72 | "filename" : "Icon-76@2x.png",
73 | "scale" : "2x"
74 | },
75 | {
76 | "size" : "83.5x83.5",
77 | "idiom" : "ipad",
78 | "filename" : "Icon-83.5@2x.png",
79 | "scale" : "2x"
80 | }
81 | ],
82 | "info" : {
83 | "version" : 1,
84 | "author" : "xcode"
85 | }
86 | }
--------------------------------------------------------------------------------
/ViperWeather/DetailListViewController.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/ViperWeather/DetailListPresenter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DetailListPresenter.swift
3 | // ViperWeather
4 | //
5 | // Created Dmitri Utmanov on 03/03/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 | // Generated via Swift-Viper templates by https://github.com/cooler333
9 | //
10 |
11 | import UIKit
12 | import Swinject
13 |
14 |
15 | protocol DetailListPresenterProtocol: class {
16 |
17 | func getDetailCity(city: City)
18 | func getWeatherForCity(city: City)
19 |
20 | func presentDetailListDetailViewController(testString: String)
21 | }
22 |
23 | protocol DetailListInterfaceProtocol: class {
24 |
25 | var presenter: DetailListPresenterProtocol! { get set }
26 |
27 | func showEmpty()
28 | func showCity(city: City)
29 | func showWeatherForCity(weather: [Weather], city: City)
30 | }
31 |
32 | class DetailListPresenter {
33 |
34 | weak private var interface: DetailListInterfaceProtocol!
35 | private let interactor: DetailListInteractorInputProtocol
36 | private let router: DetailListRouterInputProtocol
37 |
38 |
39 | init(interface: DetailListInterfaceProtocol, interactor: DetailListInteractorInputProtocol, router: DetailListRouterInputProtocol) {
40 | self.interface = interface
41 | self.interactor = interactor
42 | self.router = router
43 | }
44 | }
45 |
46 |
47 | extension DetailListPresenter: DetailListPresenterProtocol {
48 |
49 | func getDetailCity(city: City) {
50 | self.interactor.getDetailCity(city)
51 | }
52 |
53 | func getWeatherForCity(city: City) {
54 | self.interactor.getWeatherForCity(city)
55 | }
56 |
57 | func presentDetailListDetailViewController(testString: String) {
58 | self.router.presentDetailListDetailViewController(fromViewController: self.interface as! UIViewController)
59 | }
60 | }
61 |
62 | extension DetailListPresenter: DetailListInteractorOutputProtocol {
63 |
64 | func foundDetailCity(city: City) {
65 | guard city.isLocationEnable() == true else {
66 | self.interface.showEmpty()
67 | return
68 | }
69 | self.interface.showCity(city)
70 | }
71 |
72 | func foundWeatherForCity(weather: [Weather], city: City) {
73 | self.interface.showWeatherForCity(weather, city: city)
74 | }
75 | }
76 |
77 |
--------------------------------------------------------------------------------
/ViperWeather/DetailAssembler.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DetailAssembler.swift
3 | // ViperWeather
4 | //
5 | // Created by Dmitriy Utmanov on 29/02/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Swinject
11 |
12 |
13 | class DetailAssembler: Assembler {
14 |
15 | required init(parentAssembler: Assembler) {
16 | try! super.init(assemblies: [DetailContainer()], parentAssembler: parentAssembler)
17 | }
18 | }
19 |
20 | extension DetailAssembler {
21 |
22 | func presentDetailViewController(fromViewController fromViewController: UIViewController, city: City) {
23 | let viewController = self.viewController()
24 | viewController.city = city
25 |
26 | fromViewController.navigationController!.pushViewController(viewController, animated: true)
27 | }
28 |
29 | func presentDetailViewController(fromViewController fromViewController: UIViewController, inView view: UIView, city: City) {
30 | let childViewController = self.viewController()
31 | childViewController.city = city
32 |
33 | fromViewController.addChildViewController(childViewController)
34 | view.addSubview(childViewController.view)
35 |
36 | childViewController.view.translatesAutoresizingMaskIntoConstraints = false
37 |
38 | let childView = childViewController.view
39 | let views: [String : AnyObject] = ["childView": childView]
40 |
41 | var childViewLayoutConstraint: [NSLayoutConstraint] = []
42 | childViewLayoutConstraint += NSLayoutConstraint.constraintsWithVisualFormat("|-(0)-[childView]-(0)-|", options: [], metrics: nil, views: views)
43 | childViewLayoutConstraint += NSLayoutConstraint.constraintsWithVisualFormat("V:|-(0)-[childView]-(0)-|", options: [], metrics: nil, views: views)
44 | NSLayoutConstraint.activateConstraints(childViewLayoutConstraint)
45 |
46 | childViewController.didMoveToParentViewController(fromViewController)
47 | }
48 |
49 | func storyboard() -> SwinjectStoryboard {
50 | return SwinjectStoryboard.create(name: "Detail", bundle: NSBundle.mainBundle(), container: resolver)
51 | }
52 |
53 | func viewController() -> DetailViewController {
54 | return storyboard().instantiateViewControllerWithIdentifier("DetailViewControllerID") as! DetailViewController
55 | }
56 |
57 | }
--------------------------------------------------------------------------------
/ViperWeather/DetailListDetailContainer.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DetailListDetailContainer.swift
3 | // ViperWeather
4 | //
5 | // Created Dima on 16/03/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 | // Generated via Swift-Viper templates by https://github.com/cooler333
9 | //
10 |
11 | import UIKit
12 | import Swinject
13 |
14 |
15 | class DetailListDetailContainer: AssemblyType {
16 |
17 | func assemble(container: Container) {
18 | container.registerForStoryboard(DetailListDetailViewController.self) { (r, c) -> () in
19 | container.register(DetailListDetailPresenterProtocol.self) { [weak c] r in
20 | guard let c = c else { fatalError("ViewController is nil") }
21 |
22 | let interface = c
23 | let interactor = r.resolve(DetailListDetailInteractorInputProtocol.self)!
24 | let router = r.resolve(DetailListDetailRouterInputProtocol.self)!
25 |
26 | let presenter = DetailListDetailPresenter(interface: interface, interactor: interactor, router: router)
27 | interactor.presenter = presenter
28 |
29 | return presenter
30 | }
31 | c.presenter = r.resolve(DetailListDetailPresenterProtocol.self)
32 | }
33 |
34 | container.register(DetailListDetailInteractorInputProtocol.self) { r in
35 | let interactor = DetailListDetailInteractor()
36 | let dataManager = r.resolve(DetailListDetailDataManagerInputProtocol.self)!
37 | interactor.dataManager = dataManager
38 | dataManager.interactor = interactor
39 | return interactor
40 | }
41 |
42 | container.register(DetailListDetailRouterInputProtocol.self) { (r) in
43 | let router = DetailListDetailRouter()
44 | router.childAssembler = r.resolve(ChildAssembler.self)!
45 | return router
46 | }
47 |
48 | container.register(DetailListDetailDataManagerInputProtocol.self) { (r) in
49 | let dataManager = DetailListDetailDataManager()
50 | return dataManager
51 | }
52 |
53 |
54 | container.register(ChildAssembler.self) { r in
55 | let parentAssembler = r.resolve(DetailListDetailAssembler.self)!
56 | return ChildAssembler(parentAssembler: parentAssembler)
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/ViperWeather/DetailListContainer.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DetailListContainer.swift
3 | // ViperWeather
4 | //
5 | // Created Dmitri Utmanov on 03/03/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 | // Generated via Swift-Viper templates by https://github.com/cooler333
9 | //
10 |
11 | import UIKit
12 | import Swinject
13 |
14 |
15 | class DetailListContainer: AssemblyType {
16 |
17 | func assemble(container: Container) {
18 | container.registerForStoryboard(DetailListViewController.self) { (r, c) -> () in
19 | container.register(DetailListPresenterProtocol.self) { [weak c] r in
20 | guard let c = c else { fatalError("Contoller is nil") }
21 |
22 | let interface = c
23 | let interactor = r.resolve(DetailListInteractorInputProtocol.self)!
24 | let router = r.resolve(DetailListRouterInputProtocol.self)!
25 |
26 | let presenter = DetailListPresenter(interface: interface, interactor: interactor, router: router)
27 | interactor.presenter = presenter
28 |
29 | return presenter
30 | }
31 | c.presenter = r.resolve(DetailListPresenterProtocol.self)
32 | }
33 |
34 | container.register(DetailListInteractorInputProtocol.self) { r in
35 | let interactor = DetailListInteractor()
36 | interactor.dataManager = r.resolve(DetailListDataManagerInputProtocol.self)!
37 | return interactor
38 | }
39 | container.register(DetailListDataManagerOutputProtocol.self) { r in
40 | r.resolve(DetailListInteractorInputProtocol.self) as! DetailListDataManagerOutputProtocol
41 | }
42 |
43 | container.register(DetailListRouterInputProtocol.self) { (r) in
44 | let router = DetailListRouter()
45 | router.detailListDetailAssembler = r.resolve(DetailListDetailAssembler.self)!
46 | return router
47 | }
48 |
49 | container.register(DetailListDataManagerInputProtocol.self) { (r) in
50 | let dataManager = DetailListDataManager()
51 | return dataManager
52 | }
53 |
54 | container.register(DetailListDetailAssembler.self) { r in
55 | let parentAssembler = r.resolve(DetailListAssembler.self)!
56 | return DetailListDetailAssembler(parentAssembler: parentAssembler)
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/ViperWeather/ListContainer.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ListContainer.swift
3 | // ViperWeather
4 | //
5 | // Created by Dmitriy Utmanov on 29/02/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Swinject
11 |
12 |
13 | class ListContainer: AssemblyType {
14 |
15 | func assemble(container: Container) {
16 | container.registerForStoryboard(ListTableViewController.self) { (r, c) -> () in
17 | container.register(ListPresenterProtocol.self) { [weak c] r in
18 | guard let c = c else { fatalError("Contoller is nil") }
19 |
20 | let interface = c
21 | let interactor = r.resolve(ListInteractorInputProtocol.self)!
22 | let router = r.resolve(ListRouterInputProtocol.self)!
23 |
24 | let presenter = ListPresenter(interface: interface, interactor: interactor, router: router)
25 | interactor.presenter = presenter
26 |
27 | return presenter
28 | }
29 | c.presenter = r.resolve(ListPresenterProtocol.self)
30 | }
31 |
32 | container.register(ListInteractorInputProtocol.self) { r in
33 | let interactor = ListInteractor()
34 | let dataManager = r.resolve(ListDataManagerInputProtocol.self)!
35 | interactor.dataManager = dataManager
36 | dataManager.interactor = interactor
37 | return interactor
38 | }
39 |
40 | container.register(ListRouterInputProtocol.self) { (r) in
41 | let router = ListRouter()
42 | router.addAssembler = r.resolve(AddAssembler.self)!
43 | router.detailContainerAssembler = r.resolve(DetailContainerAssembler.self)!
44 | return router
45 | }
46 |
47 | container.register(ListDataManagerInputProtocol.self) { (r) in
48 | let dataManager = ListDataManager()
49 | return dataManager
50 | }
51 |
52 | container.register(AddAssembler.self) { r in
53 | let parentAssembler = r.resolve(ListAssembler.self)!
54 | return AddAssembler(parentAssembler: parentAssembler)
55 | }
56 | container.register(DetailContainerAssembler.self) { r in
57 | let parentAssembler = r.resolve(ListAssembler.self)!
58 | return DetailContainerAssembler(parentAssembler: parentAssembler)
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/ViperWeather/AddDataManager.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AddDataManager.swift
3 | // ViperWeather
4 | //
5 | // Created by Dmitri Utmanov on 01/03/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import RealmSwift
11 | import Alamofire
12 | import SwiftFetchedResultsController
13 |
14 |
15 | protocol AddDataManagerInputProtocol: class {
16 |
17 | weak var interactor: AddDataManagerOutputProtocol! { get set }
18 |
19 | func fetchCitiesWithName(name: String, callback: ([City]) -> ())
20 | func saveCityToPersistentStore(city: City)
21 | }
22 |
23 | protocol AddDataManagerOutputProtocol: class {
24 |
25 | var dataManager: AddDataManagerInputProtocol! { get set }
26 | }
27 |
28 |
29 | class AddDataManager {
30 |
31 | weak var interactor: AddDataManagerOutputProtocol!
32 | }
33 |
34 | extension AddDataManager: AddDataManagerInputProtocol {
35 |
36 | func fetchCitiesWithName(name: String, callback: ([City]) -> ()) {
37 | let method = Alamofire.Method.GET
38 | let url = "https://maps.googleapis.com/maps/api/place/autocomplete/json"
39 | let parameters = ["input": "\(name)", "types": "(cities)", "key": googleMapKey]
40 |
41 | Alamofire.Manager.sharedInstance.request(method, url, parameters: parameters, encoding: ParameterEncoding.URL, headers: nil).responseJSON { (response) -> Void in
42 | switch response.result {
43 | case .Success(let JSON):
44 | let predictions = JSON["predictions"] as! [[String: AnyObject]]
45 | var cities: [City] = []
46 | for prediction in predictions {
47 | let city = City(title: prediction["description"] as! String, ID: prediction["id"] as! String, placeID: prediction["place_id"] as! String, lat: 0.0, lng: 0.0)
48 | cities.append(city)
49 | }
50 | callback(cities)
51 |
52 | case .Failure(let error):
53 | print(error)
54 | callback([])
55 | }
56 | }
57 | }
58 |
59 | func saveCityToPersistentStore(city: City) {
60 | let realm = try! Realm()
61 | realm.beginWrite()
62 |
63 | let cityEntity = CityEntity()
64 | cityEntity.title = city.title
65 | cityEntity.ID = city.ID
66 | cityEntity.placeID = city.placeID
67 |
68 | realm.addWithNotification(cityEntity, update: false)
69 |
70 | try! realm.commitWrite()
71 | }
72 | }
--------------------------------------------------------------------------------
/ViperWeather/AddPresenter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AddPresenter.swift
3 | // Architecture
4 | //
5 | // Created Dmitri Utmanov on 19/02/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 | // Generated by Swift-Viper templates. Find latest version at https://github.com/Nikita2k/SwiftViper
9 | //
10 |
11 | import UIKit
12 |
13 |
14 | protocol AddViewControllerDelegate: class {
15 |
16 | func addViewControllerDidSelectCity(city: City)
17 | }
18 |
19 | protocol AddPresenterProtocol: class {
20 |
21 | weak var delegate: AddViewControllerDelegate? { get set }
22 |
23 | func cancel()
24 | func getCitiesWithName(name: String?)
25 | func selectCity(city: City)
26 | func selectAndSaveCity(city: City)
27 | }
28 |
29 | protocol AddInterfaceProtocol: class {
30 |
31 | var presenter: AddPresenterProtocol! { get set }
32 |
33 | func showEmpty()
34 | func showCities(cities: [City])
35 | }
36 |
37 | class AddPresenter {
38 |
39 | weak private var interface: AddInterfaceProtocol!
40 | private let interactor: AddInteractorInputProtocol
41 | private let router: AddRouterInputProtocol
42 |
43 | weak var delegate: AddViewControllerDelegate?
44 |
45 |
46 | init(interface: AddInterfaceProtocol, interactor: AddInteractorInputProtocol, router: AddRouterInputProtocol) {
47 | self.interface = interface
48 | self.interactor = interactor
49 | self.router = router
50 | }
51 | }
52 |
53 | extension AddPresenter: AddPresenterProtocol {
54 |
55 | func cancel() {
56 | self.router.closeAddViewController(viewController: self.interface as! UIViewController)
57 | }
58 |
59 | func getCitiesWithName(name: String?) {
60 | guard let name = name else {
61 | self.interface.showEmpty()
62 | return
63 | }
64 | guard name.isEmpty != true else {
65 | self.interface.showEmpty()
66 | return
67 | }
68 | self.interactor.getCitiesWithName(name)
69 | }
70 |
71 | func selectAndSaveCity(city: City) {
72 | self.interactor.saveCity(city)
73 | self.router.closeAddViewController(viewController: interface as! UIViewController)
74 | }
75 |
76 | func selectCity(city: City) {
77 | self.delegate?.addViewControllerDidSelectCity(city)
78 | self.router.closeAddViewController(viewController: interface as! UIViewController)
79 | }
80 | }
81 |
82 | extension AddPresenter: AddInteractorOutputProtocol {
83 |
84 | func foundCities(cities: [City]) {
85 | self.interface.showCities(cities)
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/ViperWeather/DetailListAssembler.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DetailListAssembler.swift
3 | // ViperWeather
4 | //
5 | // Created Dmitri Utmanov on 03/03/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 | // Generated via Swift-Viper templates by https://github.com/cooler333
9 | //
10 |
11 | import UIKit
12 | import Swinject
13 |
14 |
15 | class DetailListAssembler: Assembler {
16 |
17 | required init(parentAssembler: Assembler) {
18 | try! super.init(assemblies: [DetailListContainer()], parentAssembler: parentAssembler)
19 | }
20 | }
21 |
22 | extension DetailListAssembler {
23 |
24 | func presentDetailListViewController(fromViewController fromViewController: UIViewController, city: City) {
25 |
26 | let viewController = self.viewController()
27 | viewController.city = city
28 |
29 | fromViewController.navigationController!.pushViewController(viewController, animated: true)
30 | }
31 |
32 | func presentDetailListViewController(fromViewController fromViewController: UIViewController, inView view: UIView, city: City) {
33 | let childViewController = self.viewController()
34 | childViewController.city = city
35 |
36 | fromViewController.addChildViewController(childViewController)
37 | childViewController.view.frame = view.frame
38 | view.addSubview(childViewController.view)
39 |
40 | childViewController.view.translatesAutoresizingMaskIntoConstraints = false
41 |
42 | let childView = childViewController.view
43 | let views: [String : AnyObject] = ["childView": childView]
44 |
45 | var childViewLayoutConstraint: [NSLayoutConstraint] = []
46 | childViewLayoutConstraint += NSLayoutConstraint.constraintsWithVisualFormat("|-(0)-[childView]-(0)-|", options: [], metrics: nil, views: views)
47 | childViewLayoutConstraint += NSLayoutConstraint.constraintsWithVisualFormat("V:|-(0)-[childView]-(0)-|", options: [], metrics: nil, views: views)
48 | NSLayoutConstraint.activateConstraints(childViewLayoutConstraint)
49 |
50 | childViewController.didMoveToParentViewController(fromViewController)
51 | }
52 |
53 | func storyboard() -> SwinjectStoryboard {
54 | let storyboardName = "Detail"
55 | let bundle = NSBundle.mainBundle()
56 | return SwinjectStoryboard.create(name: storyboardName, bundle: bundle, container: resolver)
57 | }
58 |
59 | func viewController() -> DetailListViewController {
60 | return storyboard().instantiateViewControllerWithIdentifier("DetailListViewControllerID") as! DetailListViewController
61 | }
62 |
63 | }
--------------------------------------------------------------------------------
/ViperWeather/DetailContainerContainer.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DetailContainerContainer.swift
3 | // ViperWeather
4 | //
5 | // Created Dmitri Utmanov on 03/03/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 | // Generated via Swift-Viper templates by https://github.com/cooler333
9 | //
10 |
11 | import UIKit
12 | import Swinject
13 |
14 |
15 | class DetailContainerContainer: AssemblyType {
16 |
17 | func assemble(container: Container) {
18 | container.registerForStoryboard(DetailContainerViewController.self) { (r, c) -> () in
19 | container.register(DetailContainerPresenterProtocol.self) { [weak c] r in
20 | guard let c = c else { fatalError("Contoller is nil") }
21 |
22 | let interface = c
23 | let interactor = r.resolve(DetailContainerInteractorInputProtocol.self)!
24 | let router = r.resolve(DetailContainerRouterInputProtocol.self)!
25 |
26 | let presenter = DetailContainerPresenter(interface: interface, interactor: interactor, router: router)
27 | interactor.presenter = presenter
28 |
29 | return presenter
30 | }
31 | c.presenter = r.resolve(DetailContainerPresenterProtocol.self)
32 | }
33 |
34 | container.register(DetailContainerInteractorInputProtocol.self) { r in
35 | let interactor = DetailContainerInteractor()
36 | let dataManager = r.resolve(DetailContainerDataManagerInputProtocol.self)!
37 | interactor.dataManager = dataManager
38 | dataManager.interactor = interactor
39 | return interactor
40 | }
41 |
42 | container.register(DetailContainerRouterInputProtocol.self) { (r) in
43 | let router = DetailContainerRouter()
44 | router.detailAssembler = r.resolve(DetailAssembler.self)!
45 | router.detailListAssembler = r.resolve(DetailListAssembler.self)!
46 | return router
47 | }
48 |
49 | container.register(DetailContainerDataManagerInputProtocol.self) { (r) in
50 | let dataManager = DetailContainerDataManager()
51 | return dataManager
52 | }
53 |
54 | container.register(DetailAssembler.self) { r in
55 | let parentAssembler = r.resolve(DetailContainerAssembler.self)!
56 | return DetailAssembler(parentAssembler: parentAssembler)
57 | }
58 | container.register(DetailListAssembler.self) { r in
59 | let parentAssembler = r.resolve(DetailContainerAssembler.self)!
60 | return DetailListAssembler(parentAssembler: parentAssembler)
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/ViperWeather/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en_US
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 7
23 | Fabric
24 |
25 | APIKey
26 | ff2a57e35b77effb71f8b54dbecfdf36754b0962
27 | Kits
28 |
29 |
30 | KitInfo
31 |
32 | KitName
33 | Crashlytics
34 |
35 |
36 |
37 | LSApplicationCategoryType
38 |
39 | LSRequiresIPhoneOS
40 |
41 | NSAppTransportSecurity
42 |
43 | NSExceptionDomains
44 |
45 | api.openweathermap.org
46 |
47 | NSTemporaryExceptionAllowsInsecureHTTPLoads
48 |
49 |
50 | openweathermap.org
51 |
52 | NSTemporaryExceptionAllowsInsecureHTTPLoads
53 |
54 |
55 |
56 |
57 | UILaunchStoryboardName
58 | LaunchScreen
59 | UIMainStoryboardFile
60 | Main
61 | UIRequiredDeviceCapabilities
62 |
63 | armv7
64 |
65 | UIStatusBarStyle
66 | UIStatusBarStyleLightContent
67 | UIStatusBarTintParameters
68 |
69 | UINavigationBar
70 |
71 | Style
72 | UIBarStyleDefault
73 | Translucent
74 |
75 |
76 |
77 | UISupportedInterfaceOrientations
78 |
79 | UIInterfaceOrientationPortrait
80 | UIInterfaceOrientationLandscapeLeft
81 | UIInterfaceOrientationLandscapeRight
82 |
83 | UISupportedInterfaceOrientations~ipad
84 |
85 | UIInterfaceOrientationPortrait
86 | UIInterfaceOrientationPortraitUpsideDown
87 | UIInterfaceOrientationLandscapeLeft
88 | UIInterfaceOrientationLandscapeRight
89 |
90 | UIViewControllerBasedStatusBarAppearance
91 |
92 |
93 |
94 |
--------------------------------------------------------------------------------
/ViperWeather/List.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/ViperWeather/RootViewController.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/ViperWeather/MaterialColor.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MaterialColor.swift
3 | // ViperWeather
4 | //
5 | // Created by Dmitri Utmanov on 02/03/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class MaterialColor: UIColor {
12 |
13 |
14 | private class func colorWithHex(hex: Int, alpha: CGFloat = 1.0) -> UIColor {
15 | let red = CGFloat((hex & 0xFF0000) >> 16) / 255.0
16 | let green = CGFloat((hex & 0xFF00) >> 8) / 255.0
17 | let blue = CGFloat((hex & 0xFF)) / 255.0
18 | return UIColor(red:red, green:green, blue:blue, alpha:alpha)
19 | }
20 |
21 |
22 |
23 | // Some convenience methods to create colors. These colors will be as calibrated as possible.
24 | // These colors are cached.
25 | override class func darkGrayColor() -> UIColor {
26 | return MaterialColor.colorWithHex(0x424242)
27 | }
28 | override class func lightGrayColor() -> UIColor {
29 | return MaterialColor.colorWithHex(0xBDBDBD)
30 | }
31 | override class func blackColor() -> UIColor {
32 | return MaterialColor.colorWithHex(0x212121)
33 | }
34 | override class func grayColor() -> UIColor {
35 | return MaterialColor.colorWithHex(0x9E9E9E)
36 | }
37 | override class func redColor() -> UIColor {
38 | return MaterialColor.colorWithHex(0xF44336)
39 | }
40 | override class func greenColor() -> UIColor {
41 | return MaterialColor.colorWithHex(0x4CAF50)
42 | }
43 | override class func blueColor() -> UIColor {
44 | return MaterialColor.colorWithHex(0x2196F3)
45 | }
46 | override class func cyanColor() -> UIColor {
47 | return MaterialColor.colorWithHex(0x00BCD4)
48 | }
49 | override class func yellowColor() -> UIColor {
50 | return MaterialColor.colorWithHex(0xFFEB3B)
51 | }
52 | override class func magentaColor() -> UIColor {
53 | return MaterialColor.colorWithHex(0xF06292)
54 | }
55 | override class func orangeColor() -> UIColor {
56 | return MaterialColor.colorWithHex(0xFF9800)
57 | }
58 | override class func purpleColor() -> UIColor {
59 | return MaterialColor.colorWithHex(0x9C27B0)
60 | }
61 | override class func brownColor() -> UIColor {
62 | return MaterialColor.colorWithHex(0x795548)
63 | }
64 |
65 | class func deepPurpleColor() -> UIColor {
66 | return MaterialColor.colorWithHex(0x673AB7)
67 | }
68 | class func pinkColor() -> UIColor {
69 | return MaterialColor.colorWithHex(0xE91E63)
70 | }
71 | class func indigoColor() -> UIColor {
72 | return MaterialColor.colorWithHex(0x3F51B5)
73 | }
74 | class func lightBlueColor() -> UIColor {
75 | return MaterialColor.colorWithHex(0x03A9F4)
76 | }
77 | class func tealColor() -> UIColor {
78 | return MaterialColor.colorWithHex(0x009688)
79 | }
80 | class func lightGreenColor() -> UIColor {
81 | return MaterialColor.colorWithHex(0x8BC34A)
82 | }
83 | class func limeColor() -> UIColor {
84 | return MaterialColor.colorWithHex(0xCDDC39)
85 | }
86 | class func amberColor() -> UIColor {
87 | return MaterialColor.colorWithHex(0xFFC107)
88 | }
89 | class func deepOrangeColor() -> UIColor {
90 | return MaterialColor.colorWithHex(0xFF5722)
91 | }
92 | class func blueGrayColor() -> UIColor {
93 | return MaterialColor.colorWithHex(0x607D8B)
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/ViperWeather/DetailViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DetailViewController.swift
3 | // ViperWeather
4 | //
5 | // Created by Dmitri Utmanov on 02/03/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class DetailViewController: UIViewController {
12 |
13 | // MARK: - VIPER Properties
14 | var presenter: DetailPresenterProtocol!
15 |
16 | var city: City?
17 |
18 | @IBOutlet weak var weatherLabel: UILabel!
19 | @IBOutlet weak var weatherIconImageView: UIImageView!
20 | @IBOutlet weak var weatherContainerView: UIView!
21 |
22 | @IBOutlet weak var activityIndicatorView: UIActivityIndicatorView!
23 |
24 |
25 | override var nibName: String? {
26 | get {
27 | let classString = String(self.dynamicType)
28 | return classString
29 | }
30 | }
31 | override var nibBundle: NSBundle? {
32 | get {
33 | return NSBundle.mainBundle()
34 | }
35 | }
36 |
37 |
38 | override func viewDidLoad() {
39 | super.viewDidLoad()
40 |
41 | // Do any additional setup after loading the view.
42 | activityIndicatorView.color = MaterialColor.cyanColor()
43 | }
44 |
45 | override func viewWillAppear(animated: Bool) {
46 | super.viewWillAppear(animated)
47 |
48 | if let city = city {
49 | if city.isLocationEnable() == false {
50 | self.weatherContainerView.hidden = true
51 | self.activityIndicatorView.hidden = false
52 | self.presenter.getDetailCity(city)
53 | } else {
54 | self.weatherContainerView.hidden = true
55 | self.activityIndicatorView.hidden = false
56 | self.presenter.getWeatherForCity(city)
57 | }
58 | }
59 | }
60 | override func didReceiveMemoryWarning() {
61 | super.didReceiveMemoryWarning()
62 | // Dispose of any resources that can be recreated.
63 | }
64 | }
65 |
66 | extension DetailViewController: DetailInterfaceProtocol {
67 |
68 | func showEmpty() {
69 | self.city = nil
70 | }
71 |
72 | func showCity(city: City) {
73 | self.city = city
74 |
75 | self.weatherContainerView.hidden = true
76 | self.activityIndicatorView.hidden = false
77 |
78 | self.presenter.getWeatherForCity(city)
79 | }
80 |
81 | func showWeatherForCity(weather: [Weather], city: City) {
82 | self.weatherContainerView.hidden = false
83 | self.activityIndicatorView.hidden = true
84 |
85 | if weather.count > 0 {
86 | let currentWeather = weather[0]
87 | self.weatherLabel.text = currentWeather.tempString
88 |
89 | let request = NSURLRequest(URL: NSURL(string: "http://openweathermap.org/img/w/\(currentWeather.icon).png")!)
90 | NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue(), completionHandler: { [weak self] (response, data, error) -> Void in
91 | if error == nil {
92 | guard let data = data else { return }
93 | let image = UIImage(data: data)
94 | self?.weatherIconImageView.image = image
95 | } else {
96 | // Handle error
97 | }
98 | })
99 | }
100 | self.city = city
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/ViperWeather/DetailListViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DetailListViewController.swift
3 | // ViperWeather
4 | //
5 | // Created Dmitri Utmanov on 03/03/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 | // Generated via Swift-Viper templates by https://github.com/cooler333
9 | //
10 |
11 | import UIKit
12 |
13 |
14 | class DetailListViewController: UITableViewController {
15 |
16 | let kWeatherTableViewCellReuseIdentifier = "WeatherTableViewCellReuseIdentifier"
17 |
18 |
19 | // MARK: - VIPER Properties
20 | var presenter: DetailListPresenterProtocol!
21 |
22 | var city: City!
23 | var weatherList: [Weather] = []
24 |
25 |
26 | override var nibName: String? {
27 | get {
28 | let classString = String(self.dynamicType)
29 | return classString
30 | }
31 | }
32 | override var nibBundle: NSBundle? {
33 | get {
34 | return NSBundle.mainBundle()
35 | }
36 | }
37 |
38 |
39 | override func viewDidLoad() {
40 | super.viewDidLoad()
41 |
42 | tableView.registerNib(UINib(nibName: "WeatherTableViewCell", bundle: NSBundle.mainBundle()), forCellReuseIdentifier: kWeatherTableViewCellReuseIdentifier)
43 | tableView.estimatedRowHeight = UITableViewAutomaticDimension
44 | tableView.rowHeight = UITableViewAutomaticDimension
45 | // Do any additional setup after loading the view.
46 | }
47 |
48 | override func viewWillAppear(animated: Bool) {
49 | super.viewWillAppear(animated)
50 |
51 | if let city = city {
52 | if city.isLocationEnable() == false {
53 | self.presenter.getDetailCity(city)
54 | } else {
55 | self.presenter.getWeatherForCity(city)
56 | }
57 | }
58 | }
59 |
60 | override func didReceiveMemoryWarning() {
61 | super.didReceiveMemoryWarning()
62 | // Dispose of any resources that can be recreated.
63 | }
64 | }
65 |
66 | extension DetailListViewController {
67 |
68 | override func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
69 | return UITableViewAutomaticDimension
70 | }
71 |
72 | override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
73 | return UITableViewAutomaticDimension
74 | }
75 |
76 | override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
77 | return weatherList.count
78 | }
79 |
80 | override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
81 | let cell = tableView.dequeueReusableCellWithIdentifier(kWeatherTableViewCellReuseIdentifier, forIndexPath: indexPath) as! WeatherTableViewCell
82 | cell.weather = weatherList[indexPath.row]
83 | return cell
84 | }
85 |
86 | override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
87 | /*
88 |
89 | // Uncomment this line to find out how Template does work
90 | let weather = weatherList[indexPath.row]
91 | self.presenter.presentDetailListDetailViewController(weather.tempString)
92 |
93 | */
94 | }
95 | }
96 |
97 | extension DetailListViewController: DetailListInterfaceProtocol {
98 |
99 | func showEmpty() {
100 | self.city = nil
101 | self.weatherList.removeAll()
102 | }
103 |
104 | func showCity(city: City) {
105 | self.city = city
106 |
107 | self.presenter.getWeatherForCity(city)
108 | }
109 |
110 | func showWeatherForCity(weather: [Weather], city: City) {
111 | self.city = city
112 | self.weatherList = weather
113 |
114 | self.tableView.reloadData()
115 | }
116 |
117 | }
118 |
--------------------------------------------------------------------------------
/ViperWeather/DetailDataManager.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DetailDataManager.swift
3 | // ViperWeather
4 | //
5 | // Created by Dmitri Utmanov on 02/03/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import RealmSwift
11 | import Alamofire
12 | import SwiftFetchedResultsController
13 |
14 |
15 | protocol DetailDataManagerInputProtocol: class {
16 |
17 | weak var interactor: DetailDataManagerOutputProtocol! { get set }
18 |
19 | func getDetailCity(city: City, callback: (City) -> ())
20 | func getWeatherForCity(city: City, callback: ([Weather]) -> ())
21 | func updateCityInPersistentStore(city: City)
22 | }
23 |
24 | protocol DetailDataManagerOutputProtocol: class {
25 |
26 | var dataManager: DetailDataManagerInputProtocol! { get set }
27 | }
28 |
29 |
30 | class DetailDataManager {
31 |
32 | weak var interactor: DetailDataManagerOutputProtocol!
33 | }
34 |
35 | extension DetailDataManager: DetailDataManagerInputProtocol {
36 |
37 | func getDetailCity(city: City, callback: (City) -> ()) {
38 | let method = Alamofire.Method.GET
39 | let url = "https://maps.googleapis.com/maps/api/place/details/json"
40 | let parameters = ["placeid": "\(city.placeID)", "key": googleMapKey]
41 |
42 | Alamofire.Manager.sharedInstance.request(method, url, parameters: parameters, encoding: ParameterEncoding.URL, headers: nil).responseJSON { (response) -> Void in
43 | switch response.result {
44 | case .Success(let JSON):
45 | let result = JSON["result"] as! [String: AnyObject]
46 | let geometry = result["geometry"] as! [String: AnyObject]
47 | let location = geometry["location"] as! [String: AnyObject]
48 | let lat = location["lat"] as! Double
49 | let lng = location["lng"] as! Double
50 | let city = City(title: city.title, ID: city.ID, placeID: city.placeID, lat: lat, lng: lng)
51 | callback(city)
52 |
53 | case .Failure(let error):
54 | print(error)
55 | callback(city)
56 | }
57 | }
58 | }
59 |
60 | func getWeatherForCity(city: City, callback: ([Weather]) -> ()) {
61 | let method = Alamofire.Method.GET
62 | let url = "http://api.openweathermap.org/data/2.5/forecast"
63 | let parameters: [String: AnyObject] = ["lat": city.lat, "lon": city.lng, "units": "metric", "cnt": 1, "APPID": openWeatherMapKey]
64 |
65 | Alamofire.Manager.sharedInstance.request(method, url, parameters: parameters, encoding: ParameterEncoding.URL, headers: nil).responseJSON { (response) -> Void in
66 | switch response.result {
67 | case .Success(let JSON):
68 | guard let list = JSON["list"] as? [[String: AnyObject]] else {
69 | callback([])
70 | return
71 | }
72 | var weatherList: [Weather] = []
73 | for weatherData in list {
74 | let weather = Weather(dt: weatherData["dt"] as! Double, temp: weatherData["main"]!["temp"] as! Double, pressure: weatherData["main"]!["pressure"] as! Double, icon: weatherData["weather"]![0]["icon"] as! String)
75 | weatherList.append(weather)
76 | }
77 | callback(weatherList)
78 |
79 | case .Failure(let error):
80 | print(error)
81 | callback([])
82 | }
83 | }
84 | }
85 |
86 | func updateCityInPersistentStore(city: City) {
87 | let realm = try! Realm()
88 |
89 | realm.beginWrite()
90 | let predicate = NSPredicate(format: "ID = %@", argumentArray: [city.ID])
91 | let cityEntities = realm.objects(CityEntity).filter(predicate)
92 |
93 | for cityEntity in cityEntities {
94 | cityEntity.lat = city.lat
95 | cityEntity.lng = city.lng
96 | }
97 |
98 | realm.addWithNotification(cityEntities, update: true)
99 |
100 | try! realm.commitWrite()
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/ViperWeather/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // ViperWeather
4 | //
5 | // Created by Dmitriy Utmanov on 29/02/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import RealmSwift
11 |
12 |
13 | @UIApplicationMain
14 | class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDelegate {
15 |
16 | var window: UIWindow?
17 | let serviceLocatorAssembler = ServiceLocatorAssembler()
18 |
19 | func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
20 | // Override point for customization after application launch.
21 | self.configureAppearance()
22 |
23 | let config = Realm.Configuration(schemaVersion: 4, migrationBlock: { migration, oldSchemaVersion in
24 | // We haven’t migrated anything yet, so oldSchemaVersion == 0
25 | if (oldSchemaVersion < 1) {
26 | // Nothing to do!
27 | // Realm will automatically detect new properties and removed properties
28 | // And will update the schema on disk automatically
29 | }
30 | })
31 |
32 | // Tell Realm to use this new configuration object for the default Realm
33 | Realm.Configuration.defaultConfiguration = config
34 |
35 | let rootAssembler = serviceLocatorAssembler.resolver.resolve(RootAssembler.self)!
36 | rootAssembler.presentRootViewController(fromViewController: window!.rootViewController!)
37 |
38 | return true
39 | }
40 |
41 | func applicationWillResignActive(application: UIApplication) {
42 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
43 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
44 | }
45 |
46 | func applicationDidEnterBackground(application: UIApplication) {
47 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
48 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
49 | }
50 |
51 | func applicationWillEnterForeground(application: UIApplication) {
52 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
53 | }
54 |
55 | func applicationDidBecomeActive(application: UIApplication) {
56 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
57 | }
58 |
59 | func applicationWillTerminate(application: UIApplication) {
60 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
61 | }
62 |
63 |
64 | func configureAppearance() {
65 | window?.backgroundColor = MaterialColor.blackColor()
66 | window?.tintColor = MaterialColor.lightBlueColor()
67 |
68 | UINavigationBar.appearance().barTintColor = MaterialColor.lightBlueColor()
69 | UINavigationBar.appearance().tintColor = MaterialColor.whiteColor()
70 |
71 | let titleTextAttributes: [String : AnyObject] = [NSForegroundColorAttributeName: MaterialColor.whiteColor()]
72 | UINavigationBar.appearance().titleTextAttributes = titleTextAttributes
73 |
74 | UISearchBar.appearance().barTintColor = MaterialColor.lightBlueColor()
75 | UISearchBar.appearance().tintColor = MaterialColor.whiteColor()
76 | if #available(iOS 9.0, *) {
77 | UITextField.appearanceWhenContainedInInstancesOfClasses([UISearchBar.self]).tintColor = MaterialColor.blackColor()
78 | } else {
79 | UITextField.appearanceWhenContainedWithin(UISearchBar.self).tintColor = MaterialColor.blackColor()
80 | }
81 |
82 | UITextField.appearance().keyboardAppearance = .Dark
83 | UITextView.appearance().keyboardAppearance = .Dark
84 | }
85 |
86 | }
87 |
88 |
--------------------------------------------------------------------------------
/ViperWeather/DetailListDataManager.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DetailListDataManager.swift
3 | // ViperWeather
4 | //
5 | // Created Dmitri Utmanov on 03/03/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 | // Generated via Swift-Viper templates by https://github.com/cooler333
9 | //
10 |
11 | import Foundation
12 | import RealmSwift
13 | import Alamofire
14 | import SwiftFetchedResultsController
15 |
16 |
17 | protocol DetailListDataManagerInputProtocol: class {
18 |
19 | weak var interactor: DetailListDataManagerOutputProtocol! { get set }
20 |
21 | func getDetailCity(city: City, callback: (City) -> ())
22 | func getWeatherForCity(city: City, callback: ([Weather]) -> ())
23 | func updateCityInPersistentStore(city: City)
24 | }
25 |
26 | protocol DetailListDataManagerOutputProtocol: class {
27 |
28 | var dataManager: DetailListDataManagerInputProtocol! { get set }
29 | }
30 |
31 |
32 | class DetailListDataManager {
33 |
34 | weak var interactor: DetailListDataManagerOutputProtocol!
35 |
36 | }
37 |
38 | extension DetailListDataManager: DetailListDataManagerInputProtocol {
39 |
40 | func getDetailCity(city: City, callback: (City) -> ()) {
41 | let method = Alamofire.Method.GET
42 | let url = "https://maps.googleapis.com/maps/api/place/details/json"
43 | let parameters = ["placeid": "\(city.placeID)", "key": googleMapKey]
44 |
45 | Alamofire.Manager.sharedInstance.request(method, url, parameters: parameters, encoding: ParameterEncoding.URL, headers: nil).responseJSON { (response) -> Void in
46 | switch response.result {
47 | case .Success(let JSON):
48 | let result = JSON["result"] as! [String: AnyObject]
49 | let geometry = result["geometry"] as! [String: AnyObject]
50 | let location = geometry["location"] as! [String: AnyObject]
51 | let lat = location["lat"] as! Double
52 | let lng = location["lng"] as! Double
53 | let city = City(title: city.title, ID: city.ID, placeID: city.placeID, lat: lat, lng: lng)
54 | callback(city)
55 |
56 | case .Failure(let error):
57 | print(error)
58 | callback(city)
59 | }
60 | }
61 | }
62 |
63 | func getWeatherForCity(city: City, callback: ([Weather]) -> ()) {
64 | let delay = 0.5 * Double(NSEC_PER_SEC)
65 | let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))
66 | dispatch_after(time, dispatch_get_main_queue()) {
67 | // Workaround error 429: Too many requests
68 |
69 | let method = Alamofire.Method.GET
70 | let url = "http://api.openweathermap.org/data/2.5/forecast"
71 | let parameters: [String: AnyObject] = ["lat": city.lat, "lon": city.lng, "units": "metric", "APPID": openWeatherMapKey]
72 |
73 | Alamofire.Manager.sharedInstance.request(method, url, parameters: parameters, encoding: ParameterEncoding.URL, headers: nil).responseJSON { (response) -> Void in
74 | switch response.result {
75 | case .Success(let JSON):
76 | guard let list = JSON["list"] as? [[String: AnyObject]] else {
77 | callback([])
78 | return
79 | }
80 | var weatherList: [Weather] = []
81 | for weatherData in list {
82 | let weather = Weather(dt: weatherData["dt"] as! Double, temp: weatherData["main"]!["temp"] as! Double, pressure: weatherData["main"]!["pressure"] as! Double, icon: weatherData["weather"]![0]["icon"] as! String)
83 | weatherList.append(weather)
84 | }
85 | callback(weatherList)
86 |
87 | case .Failure(let error):
88 | print(error)
89 | callback([])
90 | }
91 | }
92 | }
93 | }
94 |
95 | func updateCityInPersistentStore(city: City) {
96 | let realm = try! Realm()
97 |
98 | realm.beginWrite()
99 | let predicate = NSPredicate(format: "ID = %@", argumentArray: [city.ID])
100 | let cityEntities = realm.objects(CityEntity).filter(predicate)
101 |
102 | for cityEntity in cityEntities {
103 | cityEntity.lat = city.lat
104 | cityEntity.lng = city.lng
105 | }
106 |
107 | realm.addWithNotification(cityEntities, update: true)
108 |
109 | try! realm.commitWrite()
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/ViperWeather/AddTableViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AddTableViewController.swift
3 | // ViperWeather
4 | //
5 | // Created by Dmitriy Utmanov on 29/02/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 |
12 | class AddTableViewController: UITableViewController {
13 |
14 | let kCityTableViewCellReuseIdentifier = "CityTableViewCellReuseIdentifier"
15 |
16 |
17 | // MARK: VIPER Properties
18 | var presenter: AddPresenterProtocol!
19 |
20 | let searchController = UISearchController(searchResultsController: nil)
21 |
22 | var cities: [City] = []
23 |
24 |
25 | override var nibName: String? {
26 | get {
27 | let classString = String(self.dynamicType)
28 | return classString
29 | }
30 | }
31 | override var nibBundle: NSBundle? {
32 | get {
33 | return NSBundle.mainBundle()
34 | }
35 | }
36 |
37 |
38 | override func viewDidLoad() {
39 | super.viewDidLoad()
40 |
41 | tableView.registerNib(UINib(nibName: "CityTableViewCell", bundle: NSBundle.mainBundle()), forCellReuseIdentifier: kCityTableViewCellReuseIdentifier)
42 |
43 | searchController.delegate = self
44 | searchController.searchResultsUpdater = self
45 | searchController.dimsBackgroundDuringPresentation = false
46 | searchController.searchBar.delegate = self
47 |
48 | tableView.tableHeaderView = searchController.searchBar
49 |
50 | definesPresentationContext = true
51 |
52 | searchController.searchBar.sizeToFit()
53 |
54 | searchController.active = true
55 | }
56 |
57 | // MARK: - Table view data source
58 |
59 | override func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
60 | return UITableViewAutomaticDimension
61 | }
62 | override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
63 | return UITableViewAutomaticDimension
64 | }
65 |
66 | override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
67 | return 1
68 | }
69 |
70 | override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
71 | return cities.count
72 | }
73 |
74 | override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
75 | let cell = tableView.dequeueReusableCellWithIdentifier(kCityTableViewCellReuseIdentifier, forIndexPath: indexPath) as! CityTableViewCell
76 | cell.city = cities[indexPath.row]
77 | return cell
78 | }
79 |
80 | override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
81 | let city = cities[indexPath.row]
82 |
83 | let alertController = UIAlertController(title: city.title, message: "Save to storage and Add to list or Add only", preferredStyle: UIAlertControllerStyle.Alert)
84 |
85 | let saveAction = UIAlertAction(title: "Save to storage", style: UIAlertActionStyle.Default) { [weak self] (action) -> Void in
86 | self?.presenter.selectAndSaveCity(city)
87 | }
88 | alertController.addAction(saveAction)
89 |
90 | let addAction = UIAlertAction(title: "Add only", style: UIAlertActionStyle.Default) { [weak self] (action) -> Void in
91 | self?.presenter.selectCity(city)
92 | }
93 | alertController.addAction(addAction)
94 |
95 | let cancelAction = UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Cancel, handler: nil)
96 | alertController.addAction(cancelAction)
97 |
98 | self.presentViewController(alertController, animated: true, completion: nil)
99 | }
100 |
101 | @IBAction func cancel(sender: AnyObject) {
102 | self.presenter.cancel()
103 | }
104 | }
105 |
106 | extension AddTableViewController: AddInterfaceProtocol {
107 |
108 | func showEmpty() {
109 | self.cities.removeAll()
110 | self.tableView.reloadData()
111 | }
112 |
113 | func showCities(cities: [City]) {
114 | self.cities.removeAll()
115 | self.cities = cities
116 | self.tableView.reloadData()
117 | }
118 | }
119 |
120 | extension AddTableViewController: UISearchControllerDelegate {
121 |
122 | func didPresentSearchController(searchController: UISearchController) {
123 | searchController.searchBar.becomeFirstResponder()
124 | }
125 | }
126 |
127 | extension AddTableViewController: UISearchBarDelegate {
128 |
129 | func searchBarCancelButtonClicked(searchBar: UISearchBar) {
130 | self.presenter.cancel()
131 | }
132 | }
133 |
134 | extension AddTableViewController: UISearchResultsUpdating {
135 |
136 | func updateSearchResultsForSearchController(searchController: UISearchController) {
137 | self.presenter.getCitiesWithName(searchController.searchBar.text!)
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/ViperWeather/WeatherTableViewCell.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
24 |
30 |
36 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/ViperWeather/DetailViewController.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/ViperWeather/Detail.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
--------------------------------------------------------------------------------
/ViperWeather/CityTableViewCell.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
24 |
30 |
36 |
42 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/ViperWeather/ListTableViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ListTableViewController.swift
3 | // ViperWeather
4 | //
5 | // Created by Dmitriy Utmanov on 29/02/16.
6 | // Copyright © 2016 Dmitriy Utmanov. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import RealmSwift
11 | import SwiftFetchedResultsController
12 |
13 |
14 | class ListTableViewController: UITableViewController {
15 |
16 | let kCityTableViewCellReuseIdentifier = "CityTableViewCellReuseIdentifier"
17 |
18 |
19 | // MARK: - VIPER Properties
20 | var presenter: ListPresenterProtocol!
21 |
22 |
23 | var cityFetchedResultsController: FetchedResultsController!
24 | var cities: [City] = []
25 |
26 | override var nibName: String? {
27 | get {
28 | let classString = String(self.dynamicType)
29 | return classString
30 | }
31 | }
32 | override var nibBundle: NSBundle? {
33 | get {
34 | return NSBundle.mainBundle()
35 | }
36 | }
37 |
38 |
39 | override func viewDidLoad() {
40 | super.viewDidLoad()
41 |
42 | tableView.registerNib(UINib(nibName: "CityTableViewCell", bundle: NSBundle.mainBundle()), forCellReuseIdentifier: kCityTableViewCellReuseIdentifier)
43 | tableView.estimatedRowHeight = UITableViewAutomaticDimension
44 | tableView.rowHeight = UITableViewAutomaticDimension
45 |
46 | self.navigationItem.rightBarButtonItems!.append(self.editButtonItem())
47 |
48 | let realm = try! Realm()
49 | let predicate = NSPredicate(format: "placeID != %@", "0")
50 | let fetchRequest = FetchRequest(realm: realm, predicate: predicate)
51 | let sortDescriptor = SortDescriptor(property: "title", ascending: true)
52 | fetchRequest.sortDescriptors = [sortDescriptor]
53 | self.cityFetchedResultsController = FetchedResultsController(fetchRequest: fetchRequest, sectionNameKeyPath: nil, cacheName: nil)
54 | self.cityFetchedResultsController!.delegate = self
55 | self.cityFetchedResultsController!.performFetch()
56 | }
57 |
58 | override func didReceiveMemoryWarning() {
59 | super.didReceiveMemoryWarning()
60 | // Dispose of any resources that can be recreated.
61 | }
62 |
63 | // MARK: - Table view data source
64 |
65 | override func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
66 | return UITableViewAutomaticDimension
67 | }
68 |
69 | override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
70 | return UITableViewAutomaticDimension
71 | }
72 |
73 | override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
74 | return 2
75 | }
76 |
77 | override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
78 | switch section {
79 | case 0:
80 | return cityFetchedResultsController.numberOfRowsForSectionIndex(section)
81 | case 1:
82 | return cities.count
83 | default:
84 | fatalError("Wrong section")
85 | }
86 | }
87 |
88 | override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
89 | switch section {
90 | case 0:
91 | return self.tableView(tableView, numberOfRowsInSection: section) == 0 ? nil : "From persistent store"
92 | case 1:
93 | return self.tableView(tableView, numberOfRowsInSection: section) == 0 ? nil : "Local"
94 | default:
95 | fatalError("Wrong section")
96 | }
97 | }
98 |
99 | override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
100 | switch indexPath.section {
101 | case 0:
102 | let cell = tableView.dequeueReusableCellWithIdentifier(kCityTableViewCellReuseIdentifier, forIndexPath: indexPath) as! CityTableViewCell
103 | if let cityEntity = cityFetchedResultsController.objectAtIndexPath(indexPath) {
104 | let city = City(title: cityEntity.title, ID: cityEntity.ID, placeID: cityEntity.placeID, lat: cityEntity.lat, lng: cityEntity.lng)
105 | cell.city = city
106 | }
107 | return cell
108 |
109 | case 1:
110 | let cell = tableView.dequeueReusableCellWithIdentifier(kCityTableViewCellReuseIdentifier, forIndexPath: indexPath) as! CityTableViewCell
111 | cell.city = cities[indexPath.row]
112 | return cell
113 |
114 | default:
115 | fatalError("Wrong indexPath")
116 | }
117 | }
118 |
119 | override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
120 | switch indexPath.section {
121 | case 0:
122 | if let cityEntity = cityFetchedResultsController.objectAtIndexPath(indexPath) {
123 | let city = City(title: cityEntity.title, ID: cityEntity.ID, placeID: cityEntity.placeID, lat: cityEntity.lat, lng: cityEntity.lng)
124 | self.presenter.showDetailCity(city)
125 | }
126 |
127 | case 1:
128 | let city = cities[indexPath.row]
129 | self.presenter.showDetailCity(city)
130 |
131 | default:
132 | fatalError("Wrong section")
133 | }
134 | }
135 |
136 | // Override to support conditional editing of the table view.
137 | override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
138 | // Return false if you do not want the specified item to be editable.
139 | return true
140 | }
141 |
142 | // Override to support editing the table view.
143 | override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
144 | if editingStyle == .Delete {
145 | // Delete the row from the data source
146 | switch indexPath.section {
147 | case 0:
148 | if let cityEntity = cityFetchedResultsController.objectAtIndexPath(indexPath) {
149 | let city = City(title: cityEntity.title, ID: cityEntity.ID, placeID: cityEntity.placeID, lat: cityEntity.lat, lng: cityEntity.lng)
150 | self.presenter.removeCity(city)
151 | }
152 | case 1:
153 | cities.removeAtIndex(indexPath.row)
154 | tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
155 | default:
156 | fatalError("Wrong section")
157 | }
158 | } else if editingStyle == .Insert {
159 | // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
160 | }
161 | }
162 |
163 | // Override to support rearranging the table view.
164 | override func tableView(tableView: UITableView, moveRowAtIndexPath fromIndexPath: NSIndexPath, toIndexPath: NSIndexPath) {
165 |
166 | }
167 |
168 | // Override to support conditional rearranging of the table view.
169 | override func tableView(tableView: UITableView, canMoveRowAtIndexPath indexPath: NSIndexPath) -> Bool {
170 | // Return false if you do not want the item to be re-orderable.
171 | return false
172 | }
173 |
174 | @IBAction func add(sender: AnyObject) {
175 | self.presenter.addNew()
176 | }
177 |
178 | @IBAction func exit(sender: AnyObject) {
179 | self.presenter.exit()
180 | }
181 | }
182 |
183 | extension ListTableViewController: ListInterfaceProtocol {
184 |
185 | }
186 |
187 | extension ListTableViewController: AddViewControllerDelegate {
188 |
189 | func addViewControllerDidSelectCity(city: City) {
190 | cities.append(city)
191 | self.tableView.reloadData()
192 | }
193 | }
194 |
195 | extension ListTableViewController: FetchedResultsControllerDelegate {
196 |
197 | func controllerWillChangeContent(controller: FetchedResultsController) {
198 | self.tableView.beginUpdates()
199 | }
200 |
201 | func controllerDidChangeObject(controller: FetchedResultsController, anObject: SafeObject, indexPath: NSIndexPath?, changeType: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) {
202 | let tableView = self.tableView
203 |
204 | switch changeType {
205 | case .Insert:
206 | tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: UITableViewRowAnimation.Fade)
207 |
208 | case .Delete:
209 | tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: UITableViewRowAnimation.Fade)
210 |
211 | case .Update:
212 | tableView.reloadRowsAtIndexPaths([indexPath!], withRowAnimation: UITableViewRowAnimation.Fade)
213 |
214 | case .Move:
215 | tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: UITableViewRowAnimation.Fade)
216 | tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: UITableViewRowAnimation.Fade)
217 | }
218 | }
219 |
220 | func controllerDidChangeSection(controller: FetchedResultsController, section: FetchResultsSectionInfo, sectionIndex: UInt, changeType: NSFetchedResultsChangeType) {
221 | let tableView = self.tableView
222 |
223 | if changeType == NSFetchedResultsChangeType.Insert {
224 | let indexSet = NSIndexSet(index: Int(sectionIndex))
225 | tableView.reloadSections(indexSet, withRowAnimation: UITableViewRowAnimation.Fade)
226 | // tableView.insertSections(indexSet, withRowAnimation: UITableViewRowAnimation.Fade)
227 | }
228 | else if changeType == NSFetchedResultsChangeType.Delete {
229 | let indexSet = NSIndexSet(index: Int(sectionIndex))
230 | tableView.reloadSections(indexSet, withRowAnimation: UITableViewRowAnimation.Fade)
231 | // tableView.deleteSections(indexSet, withRowAnimation: UITableViewRowAnimation.Fade)
232 | }
233 | }
234 |
235 | func controllerDidChangeContent(controller: FetchedResultsController) {
236 | self.tableView.endUpdates()
237 | }
238 | }
239 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright {yyyy} {name of copyright owner}
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------