├── Pods ├── PromiseKit │ ├── Extensions │ │ ├── QuartzCore │ │ │ └── Sources │ │ │ │ ├── PMKQuartzCore.h │ │ │ │ ├── CALayer+AnyPromise.h │ │ │ │ └── CALayer+AnyPromise.m │ │ ├── UIKit │ │ │ └── Sources │ │ │ │ ├── PMKUIKit.h │ │ │ │ ├── UIView+Promise.swift │ │ │ │ ├── UIViewController+AnyPromise.h │ │ │ │ ├── UIView+AnyPromise.m │ │ │ │ ├── UIView+AnyPromise.h │ │ │ │ ├── PMKAlertController.swift │ │ │ │ └── UIViewController+Promise.swift │ │ └── Foundation │ │ │ └── Sources │ │ │ ├── PMKFoundation.h │ │ │ ├── NSNotificationCenter+AnyPromise.m │ │ │ ├── afterlife.swift │ │ │ ├── NSNotificationCenter+AnyPromise.h │ │ │ ├── NSNotificationCenter+Promise.swift │ │ │ ├── NSURLSession+Promise.swift │ │ │ ├── NSTask+AnyPromise.h │ │ │ ├── NSTask+AnyPromise.m │ │ │ ├── NSObject+Promise.swift │ │ │ ├── NSURLSession+AnyPromise.h │ │ │ └── Process+Promise.swift │ ├── Sources │ │ ├── dispatch_promise.m │ │ ├── after.swift │ │ ├── after.m │ │ ├── AAA-CocoaPods-Hack.h │ │ ├── hang.m │ │ ├── race.swift │ │ ├── Promise+Properties.swift │ │ ├── Promise+AnyPromise.swift │ │ ├── AnyPromise+Private.h │ │ ├── DispatchQueue+Promise.swift │ │ ├── join.m │ │ ├── join.swift │ │ ├── wrap.swift │ │ ├── GlobalState.m │ │ ├── NSMethodSignatureForBlock.m │ │ ├── Zalgo.swift │ │ ├── AnyPromise.m │ │ ├── PMKCallVariadicBlock.m │ │ └── when.m │ └── LICENSE ├── Target Support Files │ ├── Compass │ │ ├── Compass.modulemap │ │ ├── Compass-dummy.m │ │ ├── Compass-prefix.pch │ │ ├── Compass-umbrella.h │ │ ├── Compass.xcconfig │ │ └── Info.plist │ ├── Alamofire │ │ ├── Alamofire.modulemap │ │ ├── Alamofire-dummy.m │ │ ├── Alamofire-prefix.pch │ │ ├── Alamofire-umbrella.h │ │ ├── Alamofire.xcconfig │ │ └── Info.plist │ ├── Pods-viper │ │ ├── Pods-viper.modulemap │ │ ├── Pods-viper-dummy.m │ │ ├── Pods-viper-umbrella.h │ │ ├── Info.plist │ │ ├── Pods-viper.debug.xcconfig │ │ ├── Pods-viper.release.xcconfig │ │ └── Pods-viper-frameworks.sh │ ├── PromiseKit │ │ ├── PromiseKit.modulemap │ │ ├── PromiseKit-dummy.m │ │ ├── PromiseKit-prefix.pch │ │ ├── PromiseKit.xcconfig │ │ ├── PromiseKit-umbrella.h │ │ └── Info.plist │ └── ObjectMapper │ │ ├── ObjectMapper.modulemap │ │ ├── ObjectMapper-dummy.m │ │ ├── ObjectMapper-prefix.pch │ │ ├── ObjectMapper-umbrella.h │ │ ├── ObjectMapper.xcconfig │ │ └── Info.plist ├── Compass │ ├── Sources │ │ ├── TypeAlias.swift │ │ ├── Location.swift │ │ ├── String+Extensions.swift │ │ ├── Router.swift │ │ └── Compass.swift │ └── LICENSE.md ├── Manifest.lock ├── ObjectMapper │ ├── LICENSE │ └── Sources │ │ ├── DictionaryTransform.swift │ │ ├── TransformType.swift │ │ ├── ISO8601DateTransform.swift │ │ ├── CustomDateFormatTransform.swift │ │ ├── EnumTransform.swift │ │ ├── DataTransform.swift │ │ ├── TransformOf.swift │ │ ├── DateTransform.swift │ │ ├── DateFormatterTransform.swift │ │ ├── NSDecimalNumberTransform.swift │ │ ├── URLTransform.swift │ │ ├── EnumOperators.swift │ │ ├── MapError.swift │ │ ├── HexColorTransform.swift │ │ └── IntegerOperators.swift └── Alamofire │ ├── LICENSE │ └── Source │ ├── DispatchQueue+Alamofire.swift │ └── Notifications.swift ├── viper.xcodeproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcuserdata │ └── avilov.xcuserdatad │ └── xcschemes │ ├── xcschememanagement.plist │ └── viper.xcscheme ├── viper ├── Helpers │ ├── APIError.swift │ ├── URLExtension.swift │ └── URNBuilder.swift ├── Presentation │ ├── UserStories │ │ ├── StartScreen │ │ │ ├── View │ │ │ │ ├── StartScreenViewInput.swift │ │ │ │ ├── StartScreenViewOutput.swift │ │ │ │ └── StartScreenViewController.swift │ │ │ ├── Interactor │ │ │ │ ├── StartScreenInteractorInput.swift │ │ │ │ └── StartScreenInteractor.swift │ │ │ ├── Configurator │ │ │ │ ├── StartScreenInitializer.swift │ │ │ │ └── StartScreenConfigurator.swift │ │ │ ├── Presenter │ │ │ │ └── StartScreenPresenter.swift │ │ │ └── StartScreenFactory.swift │ │ └── DetailsScreen │ │ │ ├── View │ │ │ ├── DetailsScreenViewInput.swift │ │ │ ├── DetailsScreenViewOutput.swift │ │ │ └── DetailsScreenViewController.swift │ │ │ ├── Interactor │ │ │ ├── DetailsScreenInteractorInput.swift │ │ │ └── DetailsScreenInteractor.swift │ │ │ ├── Configurator │ │ │ ├── DetailsScreenInitializer.swift │ │ │ └── DetailsScreenConfigurator.swift │ │ │ ├── DetailsScreenFactory.swift │ │ │ └── Presenter │ │ │ └── DetailsScreenPresenter.swift │ ├── PresentationAssemblyProtocol.swift │ ├── Routing │ │ ├── ModuleInputProtocol.swift │ │ ├── ModuleFactoryProtocol.swift │ │ ├── AppRouterProtocol.swift │ │ └── AppRouter.swift │ ├── InAppNotifications │ │ ├── InAppNotificationsProvider.swift │ │ └── InAppNotificationsProviderImpl.swift │ ├── PresentationAssembly.swift │ └── ApplicationRoot │ │ └── ApplicationRoot.swift ├── Services │ ├── ApiService │ │ ├── APIService.swift │ │ ├── APIServiceImpl.swift │ │ ├── RequestBuilder.swift │ │ ├── RequestExecutorExtension.swift │ │ └── RequestExecutor.swift │ ├── WeatherService │ │ ├── WeatherService.swift │ │ └── WeatherServiceImpl.swift │ ├── ServicesAssemblyProtocol.swift │ ├── ServicesAssembly.swift │ └── ServicesAssemblyImpl.swift ├── Model │ ├── City.swift │ └── CurrentWeather.swift ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Info.plist ├── Base.lproj │ └── LaunchScreen.storyboard └── AppDelegate.swift ├── viper.xcworkspace └── contents.xcworkspacedata ├── Podfile ├── Rambafile ├── Podfile.lock └── .gitignore /Pods/PromiseKit/Extensions/QuartzCore/Sources/PMKQuartzCore.h: -------------------------------------------------------------------------------- 1 | #import "CALayer+AnyPromise.h" 2 | -------------------------------------------------------------------------------- /Pods/PromiseKit/Extensions/UIKit/Sources/PMKUIKit.h: -------------------------------------------------------------------------------- 1 | #import "UIView+AnyPromise.h" 2 | #import "UIViewController+AnyPromise.h" 3 | -------------------------------------------------------------------------------- /Pods/PromiseKit/Extensions/Foundation/Sources/PMKFoundation.h: -------------------------------------------------------------------------------- 1 | #import "NSNotificationCenter+AnyPromise.h" 2 | #import "NSURLSession+AnyPromise.h" 3 | #import "NSTask+AnyPromise.h" 4 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Compass/Compass.modulemap: -------------------------------------------------------------------------------- 1 | framework module Compass { 2 | umbrella header "Compass-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Alamofire/Alamofire.modulemap: -------------------------------------------------------------------------------- 1 | framework module Alamofire { 2 | umbrella header "Alamofire-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Compass/Compass-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Compass : NSObject 3 | @end 4 | @implementation PodsDummy_Compass 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-viper/Pods-viper.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_viper { 2 | umbrella header "Pods-viper-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/PromiseKit/PromiseKit.modulemap: -------------------------------------------------------------------------------- 1 | framework module PromiseKit { 2 | umbrella header "PromiseKit-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Alamofire/Alamofire-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Alamofire : NSObject 3 | @end 4 | @implementation PodsDummy_Alamofire 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/ObjectMapper/ObjectMapper.modulemap: -------------------------------------------------------------------------------- 1 | framework module ObjectMapper { 2 | umbrella header "ObjectMapper-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-viper/Pods-viper-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_viper : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_viper 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/PromiseKit/PromiseKit-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_PromiseKit : NSObject 3 | @end 4 | @implementation PodsDummy_PromiseKit 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/ObjectMapper/ObjectMapper-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_ObjectMapper : NSObject 3 | @end 4 | @implementation PodsDummy_ObjectMapper 5 | @end 6 | -------------------------------------------------------------------------------- /viper.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Compass/Compass-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Alamofire/Alamofire-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /viper/Helpers/APIError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // APIError.swift 3 | // viper 4 | // 5 | // Created by Sergii Avilov on 4/11/17. 6 | // Copyright © 2017 test. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct APIError: Error { 12 | 13 | let message: String 14 | 15 | } 16 | -------------------------------------------------------------------------------- /Pods/Target Support Files/ObjectMapper/ObjectMapper-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /Pods/Target Support Files/PromiseKit/PromiseKit-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /viper.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | source 'https://github.com/CocoaPods/Specs.git' 2 | 3 | platform :ios, '9.0' 4 | 5 | def default_pods 6 | use_frameworks! 7 | pod 'Alamofire' 8 | pod 'Compass' 9 | pod 'PromiseKit' 10 | pod 'ObjectMapper' 11 | end 12 | 13 | 14 | target 'viper' do 15 | default_pods 16 | end 17 | -------------------------------------------------------------------------------- /viper/Presentation/UserStories/StartScreen/View/StartScreenViewInput.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StartScreenViewInput.swift 3 | // viper 4 | // 5 | // Created by Sergii Avilov on 18/01/2017. 6 | // Copyright © 2017 Agilie. All rights reserved. 7 | // 8 | 9 | /** 10 | * Presenter -> View 11 | */ 12 | protocol StartScreenViewInput: class { 13 | 14 | } 15 | -------------------------------------------------------------------------------- /Pods/PromiseKit/Sources/dispatch_promise.m: -------------------------------------------------------------------------------- 1 | #import "AnyPromise.h" 2 | @import Dispatch; 3 | 4 | AnyPromise *dispatch_promise_on(dispatch_queue_t queue, id block) { 5 | return [AnyPromise promiseWithValue:nil].thenOn(queue, block); 6 | } 7 | 8 | AnyPromise *dispatch_promise(id block) { 9 | return dispatch_promise_on(dispatch_get_global_queue(0, 0), block); 10 | } 11 | -------------------------------------------------------------------------------- /viper/Services/ApiService/APIService.swift: -------------------------------------------------------------------------------- 1 | // 2 | // APIService.swift 3 | // viper 4 | // 5 | // Created by Sergii Avilov on 4/11/17. 6 | // Copyright © 2017 test. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import PromiseKit 11 | 12 | protocol APIService { 13 | 14 | func obtainCurrentWeather(for cityId: Int) -> Promise 15 | 16 | } 17 | -------------------------------------------------------------------------------- /viper/Model/City.swift: -------------------------------------------------------------------------------- 1 | // 2 | // City.swift 3 | // viper 4 | // 5 | // Created by Sergii Avilov on 4/11/17. 6 | // Copyright © 2017 test. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class City { 12 | 13 | let title: String 14 | let id: Int 15 | 16 | init(title: String, id: Int) { 17 | self.title = title 18 | self.id = id 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /viper/Presentation/UserStories/StartScreen/Interactor/StartScreenInteractorInput.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StartScreenInteractorInput.swift 3 | // viper 4 | // 5 | // Created by Sergii Avilov on 18/01/2017. 6 | // Copyright © 2017 Agilie. All rights reserved. 7 | // 8 | 9 | /** 10 | * Presenter -> Interactor 11 | */ 12 | protocol StartScreenInteractorInput { 13 | 14 | var cities: [City] { get } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /viper/Presentation/UserStories/DetailsScreen/View/DetailsScreenViewInput.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DetailsScreenViewInput.swift 3 | // viper 4 | // 5 | // Created by Sergii Avilov on 19/01/2017. 6 | // Copyright © 2017 Agilie. All rights reserved. 7 | // 8 | 9 | /** 10 | * Presenter -> View 11 | */ 12 | protocol DetailsScreenViewInput: class { 13 | 14 | func assignWeather(_ weather: CurrentWeather) 15 | 16 | } 17 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Compass/Compass-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double CompassVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char CompassVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Alamofire/Alamofire-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double AlamofireVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char AlamofireVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /viper/Presentation/PresentationAssemblyProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PresentationAssemblyProtocol.swift 3 | // viper 4 | // 5 | // Created by Sergii Avilov on 1/17/17. 6 | // Copyright © 2017 test. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol PresentationAssemblyProtocol { 12 | 13 | var router: AppRouterProtocol! { get } 14 | var whisper: InAppNotificationsProvider! { get } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-viper/Pods-viper-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_viperVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_viperVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Pods/Compass/Sources/TypeAlias.swift: -------------------------------------------------------------------------------- 1 | #if os(OSX) 2 | import Cocoa 3 | #else 4 | import UIKit 5 | #endif 6 | 7 | #if os(OSX) 8 | public typealias CurrentController = NSViewController 9 | 10 | func open(url: URL) { 11 | NSWorkspace.shared().open(url) 12 | } 13 | #else 14 | public typealias CurrentController = UIViewController 15 | 16 | func open(url: URL) { 17 | UIApplication.shared.openURL(url) 18 | } 19 | #endif 20 | -------------------------------------------------------------------------------- /Pods/PromiseKit/Sources/after.swift: -------------------------------------------------------------------------------- 1 | import struct Foundation.TimeInterval 2 | import Dispatch 3 | 4 | /** 5 | - Returns: A new promise that fulfills after the specified duration. 6 | */ 7 | public func after(interval: TimeInterval) -> Promise { 8 | return Promise { fulfill, _ in 9 | let when = DispatchTime.now() + interval 10 | DispatchQueue.global().asyncAfter(deadline: when, execute: fulfill) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Pods/Target Support Files/ObjectMapper/ObjectMapper-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double ObjectMapperVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char ObjectMapperVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Pods/Compass/Sources/Location.swift: -------------------------------------------------------------------------------- 1 | public struct Location { 2 | 3 | public let path: String 4 | public let arguments: [String: String] 5 | public let payload: Any? 6 | 7 | public var scheme: String { 8 | return Compass.scheme 9 | } 10 | 11 | public init(path: String, arguments: [String: String] = [:], payload: Any? = nil) { 12 | self.path = path 13 | self.arguments = arguments 14 | self.payload = payload 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /viper/Services/WeatherService/WeatherService.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WheatherService.swift 3 | // viper 4 | // 5 | // Created by Sergii Avilov on 4/11/17. 6 | // Copyright © 2017 test. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import PromiseKit 11 | 12 | protocol WeatherService { 13 | 14 | func obtainCurrentWeather(for cityId: Int) -> Promise 15 | func city(with id: Int) -> City? 16 | var cities: [City] { get } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /viper/Services/ServicesAssemblyProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ServicesAssemblyProtocol.swift 3 | // viper 4 | // 5 | // Created by Sergii Avilov on 1/17/17. 6 | // Copyright © 2017 test. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol ServicesAssemblyProtocol { 12 | 13 | var application: UIApplication { get } 14 | //All Available Services should be here like 15 | var apiService: APIService { get } 16 | var weatherService: WeatherService { get } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /viper/Presentation/Routing/ModuleInputProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ModuleInputProtocol.swift 3 | // viper 4 | // 5 | // Created by Sergii Avilov on 1/18/17. 6 | // Copyright © 2017 test. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol ModuleInputProtocol { 12 | 13 | /** 14 | * Configure module with arguments. 15 | * Calls form Module factory 16 | */ 17 | func setupInitialState(withArguments args: NamedValuesType, completion: ModuleCompletionHandler?) 18 | } 19 | -------------------------------------------------------------------------------- /viper/Presentation/InAppNotifications/InAppNotificationsProvider.swift: -------------------------------------------------------------------------------- 1 | // 2 | // InAppNotificationsProvider.swift 3 | // viper 4 | // 5 | // Created by Sergii Avilov on 1/17/17. 6 | // Copyright © 2017 test. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol InAppNotificationsProvider { 12 | 13 | func showError(err: Error) 14 | 15 | func showInfoMessage(msg: String) 16 | func showWarningMessage(msg: String) 17 | func showErrorMessage(msg: String) 18 | 19 | } 20 | -------------------------------------------------------------------------------- /viper/Presentation/UserStories/DetailsScreen/Interactor/DetailsScreenInteractorInput.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DetailsScreenInteractorInput.swift 3 | // viper 4 | // 5 | // Created by Sergii Avilov on 19/01/2017. 6 | // Copyright © 2017 Agilie. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import PromiseKit 11 | 12 | /** 13 | * Presenter -> Interactor 14 | */ 15 | protocol DetailsScreenInteractorInput { 16 | 17 | func city(with id: Int) -> City? 18 | func obtainCurrentWeather(for cityId: Int) -> Promise 19 | 20 | } 21 | -------------------------------------------------------------------------------- /viper/Presentation/UserStories/DetailsScreen/View/DetailsScreenViewOutput.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DetailsScreenViewOutput.swift 3 | // viper 4 | // 5 | // Created by Sergii Avilov on 19/01/2017. 6 | // Copyright © 2017 Agilie. All rights reserved. 7 | // 8 | 9 | /** 10 | * View -> Presenter 11 | */ 12 | protocol DetailsScreenViewOutput { 13 | 14 | func setupInitialState(withArguments args: NamedValuesType, completion: ModuleCompletionHandler?) 15 | func viewIsReady() 16 | func city(with id: Int) -> City? 17 | func requestWeather(for cityId: Int) 18 | 19 | } 20 | -------------------------------------------------------------------------------- /viper/Presentation/UserStories/StartScreen/View/StartScreenViewOutput.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StartScreenViewOutput.swift 3 | // viper 4 | // 5 | // Created by Sergii Avilov on 18/01/2017. 6 | // Copyright © 2017 Agilie. All rights reserved. 7 | // 8 | 9 | /** 10 | * View -> Presenter 11 | */ 12 | protocol StartScreenViewOutput { 13 | 14 | func setupInitialState(withArguments args: NamedValuesType, completion: ModuleCompletionHandler?) 15 | func viewIsReady() 16 | 17 | func showWeatherDetails(cityId: Int) 18 | var cities: [City] { get } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /Pods/PromiseKit/Sources/after.m: -------------------------------------------------------------------------------- 1 | #import "AnyPromise.h" 2 | @import Dispatch; 3 | @import Foundation.NSDate; 4 | @import Foundation.NSValue; 5 | 6 | /// @return A promise that fulfills after the specified duration. 7 | AnyPromise *PMKAfter(NSTimeInterval duration) { 8 | return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) { 9 | dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(duration * NSEC_PER_SEC)); 10 | dispatch_after(time, dispatch_get_global_queue(0, 0), ^{ 11 | resolve(@(duration)); 12 | }); 13 | }]; 14 | } 15 | -------------------------------------------------------------------------------- /viper/Presentation/UserStories/StartScreen/Interactor/StartScreenInteractor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StartScreenInteractor.swift 3 | // viper 4 | // 5 | // Created by Sergii Avilov on 18/01/2017. 6 | // Copyright © 2017 Agilie. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class StartScreenInteractor { 12 | 13 | var weatherService: WeatherService! 14 | 15 | } 16 | 17 | /** 18 | * Presenter -> Interactor 19 | */ 20 | extension StartScreenInteractor: StartScreenInteractorInput { 21 | 22 | var cities: [City] { 23 | return weatherService.cities 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /Rambafile: -------------------------------------------------------------------------------- 1 | ### Headers settings 2 | company: Agilie 3 | author: Sergii Avilov 4 | 5 | ### Xcode project settings 6 | project_name: viper 7 | xcodeproj_path: viper.xcodeproj 8 | 9 | ### Code generation settings section 10 | # The main project target name 11 | project_target: viper 12 | 13 | # The file path for new modules 14 | project_file_path: viper/Presentation/UserStories 15 | 16 | # The Xcode group path to new modules 17 | project_group_path: viper/Presentation/UserStories 18 | 19 | ### Templates 20 | templates: 21 | - {name: swift_viper_template, git: 'https://github.com/agilie/swift_viper_template.git'} 22 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Alamofire/Alamofire.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/Alamofire 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public" 4 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 5 | PODS_BUILD_DIR = $BUILD_DIR 6 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_ROOT = ${SRCROOT} 8 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/Alamofire 9 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 10 | SKIP_INSTALL = YES 11 | -------------------------------------------------------------------------------- /viper/Services/ServicesAssembly.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ServicesAssembly.swift 3 | // viper 4 | // 5 | // Created by Sergii Avilov on 1/17/17. 6 | // Copyright © 2017 test. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | struct ServicesAssembly { 12 | 13 | static var shared: ServicesAssemblyProtocol { 14 | return _services 15 | } 16 | 17 | // MARK: 18 | static func setup(application: UIApplication) { 19 | _services = ServicesAssemblyImpl(application: application) 20 | } 21 | 22 | // MARK: 23 | static fileprivate var _services: ServicesAssemblyProtocol! 24 | } 25 | -------------------------------------------------------------------------------- /viper.xcodeproj/xcuserdata/avilov.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | viper.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | CDE8A7391E2E6F2B00B14CCB 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Pods/Target Support Files/ObjectMapper/ObjectMapper.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/ObjectMapper 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public" 4 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 5 | PODS_BUILD_DIR = $BUILD_DIR 6 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_ROOT = ${SRCROOT} 8 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/ObjectMapper 9 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 10 | SKIP_INSTALL = YES 11 | SWIFT_VERSION = 3.0 12 | -------------------------------------------------------------------------------- /Pods/PromiseKit/Sources/AAA-CocoaPods-Hack.h: -------------------------------------------------------------------------------- 1 | // this file because CocoaPods orders headers alphabetically 2 | // in its generated Umbrella header. We need its generated 3 | // header because to ensure subspec headers are part of the 4 | // complete module. 5 | // 6 | // Without this header AnyPromise.h is imported first which then 7 | // imports PromiseKit.h, PromiseKit.h imports AnyPromise.h, which 8 | // in this scenario is ignored because it is already being 9 | // imported. 10 | // 11 | // A better technical fix would be to move the declarations in 12 | // PromiseKit.h away thus making it a real Umbrella header… 13 | 14 | #import 15 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Compass/Compass.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/Compass 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public" 4 | OTHER_LDFLAGS = -framework "Foundation" 5 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 6 | PODS_BUILD_DIR = $BUILD_DIR 7 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 8 | PODS_ROOT = ${SRCROOT} 9 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/Compass 10 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 11 | SKIP_INSTALL = YES 12 | SWIFT_VERSION = 3.0 13 | -------------------------------------------------------------------------------- /viper/Presentation/UserStories/StartScreen/Configurator/StartScreenInitializer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StartScreenInitializer.swift 3 | // viper 4 | // 5 | // Created by Sergii Avilov on 18/01/2017. 6 | // Copyright © 2017 Agilie. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class StartScreenModuleInitializer: NSObject { 12 | 13 | //Connect with object on storyboard 14 | @IBOutlet weak var startscreenViewController: StartScreenViewController! 15 | 16 | override func awakeFromNib() { 17 | 18 | let configurator = StartScreenModuleConfigurator() 19 | configurator.configureModuleForViewInput(viewInput: startscreenViewController) 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /viper/Services/ServicesAssemblyImpl.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ServicesAssemblyImpl.swift 3 | // viper 4 | // 5 | // Created by Sergii Avilov on 1/17/17. 6 | // Copyright © 2017 test. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ServicesAssemblyImpl: ServicesAssemblyProtocol { 12 | 13 | let application: UIApplication 14 | //All Available Services should be here like 15 | let apiService: APIService 16 | let weatherService: WeatherService 17 | 18 | init(application: UIApplication) { 19 | self.application = application 20 | apiService = APIServiceImpl() 21 | weatherService = WeatherServiceImpl(apiService: apiService) 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /Pods/Target Support Files/PromiseKit/PromiseKit.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/PromiseKit 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public" 4 | OTHER_LDFLAGS = -framework "Foundation" -framework "QuartzCore" -framework "UIKit" 5 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 6 | PODS_BUILD_DIR = $BUILD_DIR 7 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 8 | PODS_ROOT = ${SRCROOT} 9 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/PromiseKit 10 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 11 | SKIP_INSTALL = YES 12 | -------------------------------------------------------------------------------- /viper/Presentation/UserStories/DetailsScreen/Configurator/DetailsScreenInitializer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DetailsScreenInitializer.swift 3 | // viper 4 | // 5 | // Created by Sergii Avilov on 19/01/2017. 6 | // Copyright © 2017 Agilie. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class DetailsScreenModuleInitializer: NSObject { 12 | 13 | //Connect with object on storyboard 14 | @IBOutlet weak var detailsscreenViewController: DetailsScreenViewController! 15 | 16 | override func awakeFromNib() { 17 | 18 | let configurator = DetailsScreenModuleConfigurator() 19 | configurator.configureModuleForViewInput(viewInput: detailsscreenViewController) 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /Pods/PromiseKit/Extensions/Foundation/Sources/NSNotificationCenter+AnyPromise.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import "PMKFoundation.h" 4 | 5 | @implementation NSNotificationCenter (PromiseKit) 6 | 7 | + (AnyPromise *)once:(NSString *)name { 8 | return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) { 9 | __block id identifier = [[NSNotificationCenter defaultCenter] addObserverForName:name object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) { 10 | [[NSNotificationCenter defaultCenter] removeObserver:identifier name:name object:nil]; 11 | resolve(PMKManifold(note, note.userInfo)); 12 | }]; 13 | }]; 14 | } 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /viper/Services/ApiService/APIServiceImpl.swift: -------------------------------------------------------------------------------- 1 | // 2 | // APIServiceImpl.swift 3 | // viper 4 | // 5 | // Created by Sergii Avilov on 4/11/17. 6 | // Copyright © 2017 test. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import PromiseKit 11 | 12 | class APIServiceImpl { 13 | 14 | fileprivate let executor: RequestExecutor 15 | fileprivate let builder: RequestBuilder 16 | 17 | init() { 18 | builder = RequestBuilder() 19 | executor = RequestExecutor(requestBuilder: builder) 20 | } 21 | 22 | } 23 | 24 | extension APIServiceImpl: APIService { 25 | 26 | func obtainCurrentWeather(for cityId: Int) -> Promise { 27 | return executor.promiseQuery(request: builder.weather(for: cityId)) 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /viper/Presentation/Routing/ModuleFactoryProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ModuleFactoryProtocol.swift 3 | // viper 4 | // 5 | // Created by Sergii Avilov on 1/17/17. 6 | // Copyright © 2017 test. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | typealias NamedValuesType = Dictionary 12 | typealias ModuleCompletionHandler = ((_ : NamedValuesType?) -> Void) 13 | 14 | protocol ModuleFactoryProtocol { 15 | 16 | /** 17 | * Module URN ( ex. profile:{userID} ) 18 | */ 19 | var moduleURN: String { get } 20 | 21 | /** 22 | * Create module with arguments 23 | * Returns module root UIViewController, must implement ModuleInputProtocol. 24 | */ 25 | func createModule(arguments: NamedValuesType, completion: ModuleCompletionHandler?) -> UIViewController 26 | } 27 | -------------------------------------------------------------------------------- /viper/Presentation/UserStories/DetailsScreen/Interactor/DetailsScreenInteractor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DetailsScreenInteractor.swift 3 | // viper 4 | // 5 | // Created by Sergii Avilov on 19/01/2017. 6 | // Copyright © 2017 Agilie. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import PromiseKit 11 | 12 | class DetailsScreenInteractor { 13 | 14 | var weatherService: WeatherService! 15 | 16 | } 17 | 18 | /** 19 | * Presenter -> Interactor 20 | */ 21 | extension DetailsScreenInteractor: DetailsScreenInteractorInput { 22 | 23 | func city(with id: Int) -> City? { 24 | return weatherService.city(with: id) 25 | } 26 | 27 | func obtainCurrentWeather(for cityId: Int) -> Promise { 28 | return weatherService.obtainCurrentWeather(for: cityId) 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /viper/Presentation/PresentationAssembly.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PresentationAssembly.swift 3 | // viper 4 | // 5 | // Created by Sergii Avilov on 1/17/17. 6 | // Copyright © 2017 test. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class PresentationAssembly: PresentationAssemblyProtocol { 12 | 13 | static let shared = PresentationAssembly() 14 | 15 | var router: AppRouterProtocol! 16 | var whisper: InAppNotificationsProvider! 17 | 18 | func setup(withNavigation navigation: UINavigationController, modules: Array, urlScheme: String, services: ServicesAssemblyProtocol) { 19 | router = AppRouterImpl(withNavigation: navigation, modules: modules, urlScheme: urlScheme) 20 | whisper = InAppNotificationsProviderImpl(withNavigation: navigation) 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /Pods/Target Support Files/PromiseKit/PromiseKit-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | #import "AAA-CocoaPods-Hack.h" 14 | #import "AnyPromise.h" 15 | #import "PromiseKit.h" 16 | #import "NSNotificationCenter+AnyPromise.h" 17 | #import "NSTask+AnyPromise.h" 18 | #import "NSURLSession+AnyPromise.h" 19 | #import "PMKFoundation.h" 20 | #import "CALayer+AnyPromise.h" 21 | #import "PMKQuartzCore.h" 22 | #import "PMKUIKit.h" 23 | #import "UIView+AnyPromise.h" 24 | #import "UIViewController+AnyPromise.h" 25 | 26 | FOUNDATION_EXPORT double PromiseKitVersionNumber; 27 | FOUNDATION_EXPORT const unsigned char PromiseKitVersionString[]; 28 | 29 | -------------------------------------------------------------------------------- /viper/Helpers/URLExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // URLExtension.swift 3 | // viper 4 | // 5 | // Created by Sergii Avilov on 1/19/17. 6 | // Copyright © 2017 test. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension URL { 12 | var queryItems: [String: String]? { 13 | return URLComponents(url: self, resolvingAgainstBaseURL: false)? 14 | .queryItems? 15 | .flatMap { $0.dictionaryRepresentation } 16 | .reduce([:], +) 17 | } 18 | } 19 | 20 | extension URLQueryItem { 21 | var dictionaryRepresentation: [String: String]? { 22 | if let value = value { 23 | return [name: value] 24 | } 25 | return nil 26 | } 27 | } 28 | 29 | func + (lhs: [Key: Value], rhs: [Key: Value]) -> [Key: Value] { 30 | var result = lhs 31 | rhs.forEach{ result[$0] = $1 } 32 | return result 33 | } 34 | -------------------------------------------------------------------------------- /Pods/Compass/Sources/String+Extensions.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension String { 4 | 5 | func split(_ delimiter: String) -> [String] { 6 | let components = self.components(separatedBy: delimiter) 7 | return components != [""] ? components : [] 8 | } 9 | 10 | func queryParameters() -> [String: String] { 11 | var parameters = [String: String]() 12 | 13 | let separatorCharacters = CharacterSet(charactersIn: "&;") 14 | self.components(separatedBy: separatorCharacters).forEach { (pair) in 15 | 16 | if let equalSeparator = pair.range(of: "=") { 17 | let name = pair.substring(to: equalSeparator.lowerBound) 18 | let value = pair.substring(from: pair.index(equalSeparator.lowerBound, offsetBy: 1)) 19 | let cleaned = value.removingPercentEncoding ?? value 20 | 21 | parameters[name] = cleaned 22 | } 23 | } 24 | 25 | return parameters 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /viper/Services/ApiService/RequestBuilder.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RequestBuilder.swift 3 | // viper 4 | // 5 | // Created by Sergii Avilov on 4/11/17. 6 | // Copyright © 2017 test. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Alamofire 11 | 12 | class RequestBuilder { 13 | 14 | // MARK: 15 | let endpoint: String = "http://api.openweathermap.org" 16 | let appId: String = "9d03bbae3f696b721505542d741588b9" //whether api key 17 | 18 | // MARK: 19 | func requestUrl(path: String) -> URLConvertible { 20 | return endpoint + path 21 | } 22 | 23 | // MARK: Requests 24 | func weather(for cityId: Int) -> URLRequest? { 25 | let url = requestUrl(path: "/data/2.5/weather") 26 | let parameters: [String: Any] = ["id" : cityId, "appid": appId] 27 | return try? URLEncoding.default.encode(URLRequest(url: url, method: .get), with: parameters) 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /Pods/PromiseKit/Extensions/Foundation/Sources/afterlife.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | #if !COCOAPODS 3 | import PromiseKit 4 | #endif 5 | 6 | /** 7 | - Returns: A promise that resolves when the provided object deallocates 8 | - Important: The promise is not guarenteed to resolve immediately when the provided object is deallocated. So you cannot write code that depends on exact timing. 9 | */ 10 | public func after(life object: NSObject) -> Promise { 11 | var reaper = objc_getAssociatedObject(object, &handle) as? GrimReaper 12 | if reaper == nil { 13 | reaper = GrimReaper() 14 | objc_setAssociatedObject(object, &handle, reaper, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) 15 | } 16 | return reaper!.promise 17 | } 18 | 19 | private var handle: UInt8 = 0 20 | 21 | private class GrimReaper: NSObject { 22 | deinit { 23 | fulfill() 24 | } 25 | let (promise, fulfill, _) = Promise.pending() 26 | } 27 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Alamofire (4.4.0) 3 | - Compass (4.0.0) 4 | - ObjectMapper (2.2.5) 5 | - PromiseKit (4.1.7): 6 | - PromiseKit/Foundation (= 4.1.7) 7 | - PromiseKit/QuartzCore (= 4.1.7) 8 | - PromiseKit/UIKit (= 4.1.7) 9 | - PromiseKit/CorePromise (4.1.7) 10 | - PromiseKit/Foundation (4.1.7): 11 | - PromiseKit/CorePromise 12 | - PromiseKit/QuartzCore (4.1.7): 13 | - PromiseKit/CorePromise 14 | - PromiseKit/UIKit (4.1.7): 15 | - PromiseKit/CorePromise 16 | 17 | DEPENDENCIES: 18 | - Alamofire 19 | - Compass 20 | - ObjectMapper 21 | - PromiseKit 22 | 23 | SPEC CHECKSUMS: 24 | Alamofire: dc44b1600b800eb63da6a19039a0083d62a6a62d 25 | Compass: b7802d133b95ffde747515f0e84ed8b5153c3d53 26 | ObjectMapper: fb30f71e08470d1e5a20b199fafe1246281db898 27 | PromiseKit: 779f2e41faf62d854e7593026ddbcb0bb5c5002d 28 | 29 | PODFILE CHECKSUM: d7f346b030b5e3afef384d8a464dd313ed28abab 30 | 31 | COCOAPODS: 1.2.0 32 | -------------------------------------------------------------------------------- /Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Alamofire (4.4.0) 3 | - Compass (4.0.0) 4 | - ObjectMapper (2.2.5) 5 | - PromiseKit (4.1.7): 6 | - PromiseKit/Foundation (= 4.1.7) 7 | - PromiseKit/QuartzCore (= 4.1.7) 8 | - PromiseKit/UIKit (= 4.1.7) 9 | - PromiseKit/CorePromise (4.1.7) 10 | - PromiseKit/Foundation (4.1.7): 11 | - PromiseKit/CorePromise 12 | - PromiseKit/QuartzCore (4.1.7): 13 | - PromiseKit/CorePromise 14 | - PromiseKit/UIKit (4.1.7): 15 | - PromiseKit/CorePromise 16 | 17 | DEPENDENCIES: 18 | - Alamofire 19 | - Compass 20 | - ObjectMapper 21 | - PromiseKit 22 | 23 | SPEC CHECKSUMS: 24 | Alamofire: dc44b1600b800eb63da6a19039a0083d62a6a62d 25 | Compass: b7802d133b95ffde747515f0e84ed8b5153c3d53 26 | ObjectMapper: fb30f71e08470d1e5a20b199fafe1246281db898 27 | PromiseKit: 779f2e41faf62d854e7593026ddbcb0bb5c5002d 28 | 29 | PODFILE CHECKSUM: d7f346b030b5e3afef384d8a464dd313ed28abab 30 | 31 | COCOAPODS: 1.2.0 32 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Compass/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 4.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Alamofire/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 4.4.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/ObjectMapper/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 2.2.5 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-viper/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/PromiseKit/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 4.1.7 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/PromiseKit/Sources/hang.m: -------------------------------------------------------------------------------- 1 | #import "AnyPromise.h" 2 | #import "AnyPromise+Private.h" 3 | @import CoreFoundation.CFRunLoop; 4 | 5 | /** 6 | Suspends the active thread waiting on the provided promise. 7 | 8 | @return The value of the provided promise once resolved. 9 | */ 10 | id PMKHang(AnyPromise *promise) { 11 | if (promise.pending) { 12 | static CFRunLoopSourceContext context; 13 | 14 | CFRunLoopRef runLoop = CFRunLoopGetCurrent(); 15 | CFRunLoopSourceRef runLoopSource = CFRunLoopSourceCreate(NULL, 0, &context); 16 | CFRunLoopAddSource(runLoop, runLoopSource, kCFRunLoopDefaultMode); 17 | 18 | promise.always(^{ 19 | CFRunLoopStop(runLoop); 20 | }); 21 | while (promise.pending) { 22 | CFRunLoopRun(); 23 | } 24 | CFRunLoopRemoveSource(runLoop, runLoopSource, kCFRunLoopDefaultMode); 25 | CFRelease(runLoopSource); 26 | } 27 | 28 | return promise.value; 29 | } 30 | -------------------------------------------------------------------------------- /viper/Helpers/URNBuilder.swift: -------------------------------------------------------------------------------- 1 | // 2 | // URNBuilder.swift 3 | // viper 4 | // 5 | // Created by Sergii Avilov on 1/19/17. 6 | // Copyright © 2017 test. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct URNBuilder { 12 | 13 | let urn: String 14 | 15 | init(string: String) { 16 | urn = string 17 | } 18 | 19 | func buildWithArgs(args: Array) -> String { 20 | let components = urn.components(separatedBy: ":") 21 | 22 | var indx = 0 23 | var result = "" 24 | 25 | for part in components { 26 | if !result.isEmpty { 27 | result += ":" 28 | } 29 | if part.hasPrefix("{") && indx < args.count { 30 | result += args[indx] 31 | indx += 1 32 | } else { 33 | 34 | result += part 35 | } 36 | } 37 | 38 | return result 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /viper/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | } 43 | ], 44 | "info" : { 45 | "version" : 1, 46 | "author" : "xcode" 47 | } 48 | } -------------------------------------------------------------------------------- /viper/Presentation/UserStories/DetailsScreen/Configurator/DetailsScreenConfigurator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DetailsScreenConfigurator.swift 3 | // viper 4 | // 5 | // Created by Sergii Avilov on 19/01/2017. 6 | // Copyright © 2017 Agilie. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class DetailsScreenModuleConfigurator { 12 | 13 | func configureModuleForViewInput(viewInput: UIViewController) { 14 | 15 | if let viewController = viewInput as? DetailsScreenViewController { 16 | configure(viewController) 17 | } 18 | } 19 | 20 | private func configure(_ viewController: DetailsScreenViewController) { 21 | let presenter = DetailsScreenPresenter() 22 | presenter.view = viewController 23 | 24 | let interactor = DetailsScreenInteractor() 25 | interactor.weatherService = ServicesAssembly.shared.weatherService 26 | 27 | presenter.interactor = interactor 28 | viewController.output = presenter 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /Pods/PromiseKit/Extensions/QuartzCore/Sources/CALayer+AnyPromise.h: -------------------------------------------------------------------------------- 1 | // 2 | // CALayer+AnyPromise.h 3 | // 4 | // Created by María Patricia Montalvo Dzib on 24/11/14. 5 | // Copyright (c) 2014 Aluxoft SCP. All rights reserved. 6 | // 7 | 8 | #import 9 | #import 10 | 11 | /** 12 | To import the `CALayer` category: 13 | 14 | use_frameworks! 15 | pod "PromiseKit/QuartzCore" 16 | 17 | Or `CALayer` is one of the categories imported by the umbrella pod: 18 | 19 | use_frameworks! 20 | pod "PromiseKit" 21 | 22 | And then in your sources: 23 | 24 | @import PromiseKit; 25 | */ 26 | @interface CALayer (PromiseKit) 27 | 28 | /** 29 | Add the specified animation object to the layer’s render tree. 30 | 31 | @return A promise that thens two parameters: 32 | 33 | 1. `@YES` if the animation progressed entirely to completion. 34 | 2. The `CAAnimation` object. 35 | 36 | @see addAnimation:forKey 37 | */ 38 | - (AnyPromise *)promiseAnimation:(CAAnimation *)animation forKey:(NSString *)key; 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /Pods/Compass/Sources/Router.swift: -------------------------------------------------------------------------------- 1 | public enum RouteError: Error { 2 | case notFound 3 | case invalidArguments(Location) 4 | case invalidPayload(Location) 5 | } 6 | 7 | public protocol Routable { 8 | 9 | func navigate(to location: Location, from currentController: CurrentController) throws 10 | } 11 | 12 | public protocol ErrorRoutable { 13 | 14 | func handle(routeError error: Error, from currentController: CurrentController) 15 | } 16 | 17 | public struct Router: Routable { 18 | 19 | public var routes = [String: Routable]() 20 | public var errorRoute: ErrorRoutable? 21 | 22 | public init() {} 23 | 24 | public func navigate(to location: Location, from currentController: CurrentController) { 25 | guard let route = routes[location.path] else { 26 | errorRoute?.handle(routeError: RouteError.notFound, from: currentController) 27 | return 28 | } 29 | 30 | do { 31 | try route.navigate(to: location, from: currentController) 32 | } catch { 33 | errorRoute?.handle(routeError: error, from: currentController) 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /viper/Presentation/UserStories/StartScreen/Configurator/StartScreenConfigurator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StartScreenConfigurator.swift 3 | // viper 4 | // 5 | // Created by Sergii Avilov on 18/01/2017. 6 | // Copyright © 2017 Agilie. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class StartScreenModuleConfigurator { 12 | 13 | func configureModuleForViewInput(viewInput: UIViewController) { 14 | 15 | if let viewController = viewInput as? StartScreenViewController { 16 | configure(viewController) 17 | } 18 | } 19 | 20 | private func configure(_ viewController: StartScreenViewController) { 21 | let presenter = StartScreenPresenter() 22 | presenter.view = viewController 23 | 24 | let interactor = StartScreenInteractor() 25 | interactor.weatherService = ServicesAssembly.shared.weatherService 26 | 27 | presenter.interactor = interactor 28 | presenter.router = PresentationAssembly.shared.router 29 | viewController.output = presenter 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /Pods/ObjectMapper/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2014 Hearst 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | -------------------------------------------------------------------------------- /Pods/PromiseKit/Extensions/QuartzCore/Sources/CALayer+AnyPromise.m: -------------------------------------------------------------------------------- 1 | // 2 | // CALayer+PromiseKit.m 3 | // 4 | // Created by María Patricia Montalvo Dzib on 24/11/14. 5 | // Copyright (c) 2014 Aluxoft SCP. All rights reserved. 6 | // 7 | 8 | #import 9 | #import "CALayer+AnyPromise.h" 10 | 11 | @interface PMKCAAnimationDelegate : NSObject { 12 | @public 13 | PMKResolver resolve; 14 | CAAnimation *animation; 15 | } 16 | @end 17 | 18 | @implementation PMKCAAnimationDelegate 19 | 20 | - (void)animationDidStop:(CAAnimation *)ignoreOrRetainCycleHappens finished:(BOOL)flag { 21 | resolve(PMKManifold(@(flag), animation)); 22 | animation.delegate = nil; 23 | } 24 | 25 | @end 26 | 27 | @implementation CALayer (PromiseKit) 28 | 29 | - (AnyPromise *)promiseAnimation:(CAAnimation *)animation forKey:(NSString *)key { 30 | PMKCAAnimationDelegate *d = animation.delegate = [PMKCAAnimationDelegate new]; 31 | d->animation = animation; 32 | [self addAnimation:animation forKey:key]; 33 | return [[AnyPromise alloc] initWithResolver:&d->resolve]; 34 | } 35 | 36 | @end 37 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-viper/Pods-viper.debug.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/Alamofire" "$PODS_CONFIGURATION_BUILD_DIR/Compass" "$PODS_CONFIGURATION_BUILD_DIR/ObjectMapper" "$PODS_CONFIGURATION_BUILD_DIR/PromiseKit" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 5 | OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/Alamofire/Alamofire.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/Compass/Compass.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/ObjectMapper/ObjectMapper.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/PromiseKit/PromiseKit.framework/Headers" 6 | OTHER_LDFLAGS = $(inherited) -framework "Alamofire" -framework "Compass" -framework "ObjectMapper" -framework "PromiseKit" 7 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 8 | PODS_BUILD_DIR = $BUILD_DIR 9 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 10 | PODS_ROOT = ${SRCROOT}/Pods 11 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-viper/Pods-viper.release.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/Alamofire" "$PODS_CONFIGURATION_BUILD_DIR/Compass" "$PODS_CONFIGURATION_BUILD_DIR/ObjectMapper" "$PODS_CONFIGURATION_BUILD_DIR/PromiseKit" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 5 | OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/Alamofire/Alamofire.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/Compass/Compass.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/ObjectMapper/ObjectMapper.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/PromiseKit/PromiseKit.framework/Headers" 6 | OTHER_LDFLAGS = $(inherited) -framework "Alamofire" -framework "Compass" -framework "ObjectMapper" -framework "PromiseKit" 7 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 8 | PODS_BUILD_DIR = $BUILD_DIR 9 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 10 | PODS_ROOT = ${SRCROOT}/Pods 11 | -------------------------------------------------------------------------------- /Pods/PromiseKit/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2016, Max Howell; mxcl@me.com 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a 4 | copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included 12 | in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 18 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 19 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 20 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /Pods/Alamofire/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /viper/Presentation/UserStories/StartScreen/Presenter/StartScreenPresenter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StartScreenPresenter.swift 3 | // viper 4 | // 5 | // Created by Sergii Avilov on 18/01/2017. 6 | // Copyright © 2017 Agilie. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class StartScreenPresenter { 12 | 13 | weak var view: StartScreenViewInput! 14 | var interactor: StartScreenInteractorInput! 15 | var moduleCompletion: ModuleCompletionHandler? 16 | var router: AppRouterProtocol! 17 | 18 | } 19 | 20 | // MARK: 21 | extension StartScreenPresenter: StartScreenViewOutput { 22 | 23 | func setupInitialState(withArguments args: NamedValuesType, completion: ModuleCompletionHandler?) { 24 | moduleCompletion = completion 25 | } 26 | 27 | func viewIsReady() { 28 | 29 | } 30 | 31 | func showWeatherDetails(cityId: Int) { 32 | let urn = DetailsScreenFactory.shared.createModuleURN(cityId: cityId) 33 | self.router.pushModule(byUrn: urn, animated: true) { (parameters) in 34 | 35 | } 36 | } 37 | 38 | var cities: [City] { 39 | return interactor.cities 40 | } 41 | 42 | } 43 | 44 | 45 | -------------------------------------------------------------------------------- /viper/Services/ApiService/RequestExecutorExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RequestExecutorExtension.swift 3 | // viper 4 | // 5 | // Created by Sergii Avilov on 4/11/17. 6 | // Copyright © 2017 test. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Alamofire 11 | import PromiseKit 12 | import ObjectMapper 13 | 14 | extension RequestExecutor { 15 | 16 | func promiseQuery(request: URLRequest?) -> Promise { 17 | return Promise { (fullfit, reject) in 18 | guard let request = request else { 19 | reject(APIError(message: "Invalid request")) 20 | return 21 | } 22 | self.runRequest(request: request) { (json, err) in 23 | if let err = err { 24 | reject(err) 25 | } else { 26 | if let obj = Mapper(context: nil).map(JSONObject: json) { 27 | fullfit(obj) 28 | } else { 29 | reject(APIError(message: "Mapping error")) 30 | } 31 | } 32 | } 33 | 34 | } 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /Pods/Compass/LICENSE.md: -------------------------------------------------------------------------------- 1 | Licensed under the **MIT** license 2 | 3 | > Copyright (c) 2015 Hyper Interaktiv AS 4 | > 5 | > Permission is hereby granted, free of charge, to any person obtaining 6 | > a copy of this software and associated documentation files (the 7 | > "Software"), to deal in the Software without restriction, including 8 | > without limitation the rights to use, copy, modify, merge, publish, 9 | > distribute, sublicense, and/or sell copies of the Software, and to 10 | > permit persons to whom the Software is furnished to do so, subject to 11 | > the following conditions: 12 | > 13 | > The above copyright notice and this permission notice shall be 14 | > included in all copies or substantial portions of the Software. 15 | > 16 | > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | > EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | > MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | > IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | > CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | > TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | > SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /viper/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 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 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIRequiredDeviceCapabilities 26 | 27 | armv7 28 | 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | 33 | NSAppTransportSecurity 34 | 35 | NSAllowsArbitraryLoads 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /viper/Presentation/ApplicationRoot/ApplicationRoot.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // viper 4 | // 5 | // Created by Sergii Avilov on 1/17/17. 6 | // Copyright © 2017 test. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ApplicationRoot: NSObject { 12 | 13 | // TODO: Add all application modules here 14 | let modules : Array = [ 15 | StartScreenFactory.shared, 16 | DetailsScreenFactory.shared 17 | ] 18 | 19 | // MARK: 20 | fileprivate let router: AppRouterProtocol 21 | fileprivate let whisper: InAppNotificationsProvider 22 | 23 | init(withNavigation navigation: UINavigationController) { 24 | let services = ServicesAssembly.shared 25 | let presentation = PresentationAssembly.shared 26 | 27 | presentation.setup(withNavigation: navigation, modules: modules, urlScheme: "viper", services: services) 28 | 29 | router = presentation.router 30 | whisper = presentation.whisper 31 | 32 | super.init() 33 | } 34 | 35 | func start() { 36 | let urn = StartScreenFactory.shared.moduleURN 37 | router.pushModule(byUrn: urn, animated: true, completion: { (_) in 38 | //completion 39 | }) 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /viper/Services/WeatherService/WeatherServiceImpl.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WheatherServiceImpl.swift 3 | // viper 4 | // 5 | // Created by Sergii Avilov on 4/11/17. 6 | // Copyright © 2017 test. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import PromiseKit 11 | 12 | class WeatherServiceImpl { 13 | 14 | let apiService: APIService! 15 | lazy fileprivate var _cities: [City] = { 16 | return [ 17 | City(title: "London", id: 2643743), 18 | City(title: "New York", id: 5128638), 19 | City(title: "Paris", id: 2968815), 20 | City(title: "Kyiv", id: 703447), 21 | City(title: "California", id: 5332921), 22 | City(title: "Madrid", id: 3117735), 23 | City(title: "Berlin", id: 2950159) 24 | ] 25 | }() 26 | 27 | init(apiService: APIService) { 28 | self.apiService = apiService 29 | } 30 | 31 | } 32 | 33 | extension WeatherServiceImpl: WeatherService { 34 | 35 | func obtainCurrentWeather(for cityId: Int) -> Promise { 36 | return apiService.obtainCurrentWeather(for: cityId) 37 | } 38 | 39 | func city(with id: Int) -> City? { 40 | return _cities.first { (city) -> Bool in 41 | return city.id == id 42 | } 43 | } 44 | 45 | var cities: [City] { 46 | return _cities 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /Pods/PromiseKit/Sources/race.swift: -------------------------------------------------------------------------------- 1 | /** 2 | Resolves with the first resolving promise from a set of promises. 3 | 4 | race(promise1, promise2, promise3).then { winner in 5 | //… 6 | } 7 | 8 | - Returns: A new promise that resolves when the first promise in the provided promises resolves. 9 | - Warning: If any of the provided promises reject, the returned promise is rejected. 10 | - Warning: aborts if the array is empty. 11 | */ 12 | public func race(promises: [Promise]) -> Promise { 13 | guard promises.count > 0 else { 14 | fatalError("Cannot race with an empty array of promises") 15 | } 16 | return _race(promises: promises) 17 | } 18 | 19 | /** 20 | Resolves with the first resolving promise from a set of promises. 21 | 22 | race(promise1, promise2, promise3).then { winner in 23 | //… 24 | } 25 | 26 | - Returns: A new promise that resolves when the first promise in the provided promises resolves. 27 | - Warning: If any of the provided promises reject, the returned promise is rejected. 28 | - Warning: aborts if the array is empty. 29 | */ 30 | public func race(_ promises: Promise...) -> Promise { 31 | return _race(promises: promises) 32 | } 33 | 34 | private func _race(promises: [Promise]) -> Promise { 35 | return Promise(sealant: { resolve in 36 | for promise in promises { 37 | promise.state.pipe(resolve) 38 | } 39 | }) 40 | } 41 | -------------------------------------------------------------------------------- /Pods/ObjectMapper/Sources/DictionaryTransform.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DictionaryTransform.swift 3 | // ObjectMapper 4 | // 5 | // Created by Milen Halachev on 7/20/16. 6 | // Copyright © 2016 hearst. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | ///Transforms [String: AnyObject] <-> [Key: Value] where Key is RawRepresentable as String, Value is Mappable 12 | public struct DictionaryTransform: TransformType where Key: Hashable, Key: RawRepresentable, Key.RawValue == String, Value: Mappable { 13 | 14 | public init() { 15 | 16 | } 17 | 18 | public func transformFromJSON(_ value: Any?) -> [Key: Value]? { 19 | 20 | guard let json = value as? [String: Any] else { 21 | 22 | return nil 23 | } 24 | 25 | let result = json.reduce([:]) { (result, element) -> [Key: Value] in 26 | 27 | guard 28 | let key = Key(rawValue: element.0), 29 | let valueJSON = element.1 as? [String: Any], 30 | let value = Value(JSON: valueJSON) 31 | else { 32 | 33 | return result 34 | } 35 | 36 | var result = result 37 | result[key] = value 38 | return result 39 | } 40 | 41 | return result 42 | } 43 | 44 | public func transformToJSON(_ value: [Key: Value]?) -> Any? { 45 | 46 | let result = value?.reduce([:]) { (result, element) -> [String: Any] in 47 | 48 | let key = element.0.rawValue 49 | let value = element.1.toJSON() 50 | 51 | var result = result 52 | result[key] = value 53 | return result 54 | } 55 | 56 | return result 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Pods/PromiseKit/Extensions/Foundation/Sources/NSNotificationCenter+AnyPromise.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | 5 | /** 6 | To import the `NSNotificationCenter` category: 7 | 8 | use_frameworks! 9 | pod "PromiseKit/Foundation" 10 | 11 | Or `NSNotificationCenter` is one of the categories imported by the umbrella pod: 12 | 13 | use_frameworks! 14 | pod "PromiseKit" 15 | 16 | And then in your sources: 17 | 18 | #import 19 | */ 20 | @interface NSNotificationCenter (PromiseKit) 21 | /** 22 | Observe the named notification once. 23 | 24 | [NSNotificationCenter once:UIKeyboardWillShowNotification].then(^(id note, id userInfo){ 25 | UIViewAnimationCurve curve = [userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue]; 26 | CGFloat duration = [userInfo[UIKeyboardAnimationDurationUserInfoKey] floatValue]; 27 | 28 | return [UIView promiseWithDuration:duration delay:0.0 options:(curve << 16) animations:^{ 29 | 30 | }]; 31 | }); 32 | 33 | @warning *Important* Promises only resolve once. If you need your block to execute more than once then use `-addObserverForName:object:queue:usingBlock:`. 34 | 35 | @param notificationName The name of the notification for which to register the observer. 36 | 37 | @return A promise that fulfills with two parameters: 38 | 39 | 1. The NSNotification object. 40 | 2. The NSNotification’s userInfo property. 41 | */ 42 | + (AnyPromise *)once:(NSString *)notificationName NS_REFINED_FOR_SWIFT; 43 | 44 | @end 45 | -------------------------------------------------------------------------------- /Pods/ObjectMapper/Sources/TransformType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TransformType.swift 3 | // ObjectMapper 4 | // 5 | // Created by Syo Ikeda on 2/4/15. 6 | // 7 | // The MIT License (MIT) 8 | // 9 | // Copyright (c) 2014-2016 Hearst 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining a copy 12 | // of this software and associated documentation files (the "Software"), to deal 13 | // in the Software without restriction, including without limitation the rights 14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | // copies of the Software, and to permit persons to whom the Software is 16 | // furnished to do so, subject to the following conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be included in 19 | // all copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | // THE SOFTWARE. 28 | 29 | public protocol TransformType { 30 | associatedtype Object 31 | associatedtype JSON 32 | 33 | func transformFromJSON(_ value: Any?) -> Object? 34 | func transformToJSON(_ value: Object?) -> JSON? 35 | } 36 | -------------------------------------------------------------------------------- /Pods/PromiseKit/Sources/Promise+Properties.swift: -------------------------------------------------------------------------------- 1 | extension Promise { 2 | /** 3 | - Returns: The error with which this promise was rejected; `nil` if this promise is not rejected. 4 | */ 5 | public var error: Error? { 6 | switch state.get() { 7 | case .none: 8 | return nil 9 | case .some(.fulfilled): 10 | return nil 11 | case .some(.rejected(let error, _)): 12 | return error 13 | } 14 | } 15 | 16 | /** 17 | - Returns: `true` if the promise has not yet resolved. 18 | */ 19 | public var isPending: Bool { 20 | return state.get() == nil 21 | } 22 | 23 | /** 24 | - Returns: `true` if the promise has resolved. 25 | */ 26 | public var isResolved: Bool { 27 | return !isPending 28 | } 29 | 30 | /** 31 | - Returns: `true` if the promise was fulfilled. 32 | */ 33 | public var isFulfilled: Bool { 34 | return value != nil 35 | } 36 | 37 | /** 38 | - Returns: `true` if the promise was rejected. 39 | */ 40 | public var isRejected: Bool { 41 | return error != nil 42 | } 43 | 44 | /** 45 | - Returns: The value with which this promise was fulfilled or `nil` if this promise is pending or rejected. 46 | */ 47 | public var value: T? { 48 | switch state.get() { 49 | case .none: 50 | return nil 51 | case .some(.fulfilled(let value)): 52 | return value 53 | case .some(.rejected): 54 | return nil 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Pods/PromiseKit/Sources/Promise+AnyPromise.swift: -------------------------------------------------------------------------------- 1 | import class Dispatch.DispatchQueue 2 | 3 | extension Promise { 4 | /** 5 | The provided closure executes once this promise resolves. 6 | 7 | - Parameter on: The queue on which the provided closure executes. 8 | - Parameter body: The closure that is executed when this promise fulfills. 9 | - Returns: A new promise that resolves when the `AnyPromise` returned from the provided closure resolves. For example: 10 | 11 | NSURLSession.GET(url).then { (data: NSData) -> AnyPromise in 12 | //… 13 | return SCNetworkReachability() 14 | }.then { _ in 15 | //… 16 | } 17 | */ 18 | public func then(on q: DispatchQueue = .default, execute body: @escaping (T) throws -> AnyPromise) -> Promise { 19 | return Promise(sealant: { resolve in 20 | state.then(on: q, else: resolve) { value in 21 | try body(value).state.pipe(resolve) 22 | } 23 | }) 24 | } 25 | 26 | @available(*, unavailable, message: "unwrap the promise") 27 | public func then(on: DispatchQueue = .default, execute body: (T) throws -> AnyPromise?) -> Promise { fatalError() } 28 | } 29 | 30 | /** 31 | `firstly` can make chains more readable. 32 | */ 33 | public func firstly(execute body: () throws -> AnyPromise) -> Promise { 34 | return Promise(sealant: { resolve in 35 | do { 36 | try body().state.pipe(resolve) 37 | } catch { 38 | resolve(Resolution(error)) 39 | } 40 | }) 41 | } 42 | -------------------------------------------------------------------------------- /Pods/PromiseKit/Extensions/Foundation/Sources/NSNotificationCenter+Promise.swift: -------------------------------------------------------------------------------- 1 | import Foundation.NSNotification 2 | #if !COCOAPODS 3 | import PromiseKit 4 | #endif 5 | 6 | /** 7 | To import the `NSNotificationCenter` category: 8 | 9 | use_frameworks! 10 | pod "PromiseKit/Foundation" 11 | 12 | Or `NSNotificationCenter` is one of the categories imported by the umbrella pod: 13 | 14 | use_frameworks! 15 | pod "PromiseKit" 16 | 17 | And then in your sources: 18 | 19 | import PromiseKit 20 | */ 21 | extension NotificationCenter { 22 | /// Observe the named notification once 23 | public func observe(once name: Notification.Name, object: Any? = nil) -> NotificationPromise { 24 | let (promise, fulfill) = NotificationPromise.go() 25 | let id = addObserver(forName: name, object: object, queue: nil, using: fulfill) 26 | _ = promise.always { self.removeObserver(id) } 27 | return promise 28 | } 29 | } 30 | 31 | /// The promise returned by `NotificationCenter.observe(once:)` 32 | public class NotificationPromise: Promise<[AnyHashable: Any]> { 33 | private let pending = Promise.pending() 34 | 35 | public func asNotification() -> Promise { 36 | return pending.promise 37 | } 38 | 39 | fileprivate class func go() -> (NotificationPromise, (Notification) -> Void) { 40 | let (p, fulfill, _) = NotificationPromise.pending() 41 | let promise = p as! NotificationPromise 42 | _ = promise.pending.promise.then { fulfill($0.userInfo ?? [:]) } 43 | return (promise, promise.pending.fulfill) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Pods/PromiseKit/Extensions/Foundation/Sources/NSURLSession+Promise.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | #if !COCOAPODS 3 | import PromiseKit 4 | #endif 5 | 6 | /** 7 | To import the `NSURLSession` category: 8 | 9 | use_frameworks! 10 | pod "PromiseKit/Foundation" 11 | 12 | Or `NSURLSession` is one of the categories imported by the umbrella pod: 13 | 14 | use_frameworks! 15 | pod "PromiseKit" 16 | 17 | And then in your sources: 18 | 19 | import PromiseKit 20 | */ 21 | extension URLSession { 22 | /** 23 | Makes an HTTP request using the parameters specified by the provided URL 24 | request. 25 | 26 | We recommend the use of [OMGHTTPURLRQ] which allows you to construct correct REST requests. 27 | 28 | let rq = OMGHTTPURLRQ.POST(url, json: parameters) 29 | NSURLSession.shared.dataTask(with: rq).asDictionary().then { json in 30 | //… 31 | } 32 | 33 | [We provide OMG extensions](https://github.com/PromiseKit/OMGHTTPURLRQ) 34 | that allow eg: 35 | 36 | URLSession.shared.POST(url, json: ["a": "b"]) 37 | 38 | - Parameter request: The URL request. 39 | - Returns: A promise that represents the URL request. 40 | - SeeAlso: `URLDataPromise` 41 | - SeeAlso: [OMGHTTPURLRQ] 42 | 43 | [OMGHTTPURLRQ]: https://github.com/mxcl/OMGHTTPURLRQ 44 | */ 45 | public func dataTask(with request: URLRequest) -> URLDataPromise { 46 | return URLDataPromise.go(request) { completionHandler in 47 | dataTask(with: request, completionHandler: completionHandler).resume() 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /viper/Presentation/InAppNotifications/InAppNotificationsProviderImpl.swift: -------------------------------------------------------------------------------- 1 | // 2 | // InAppNotificationsProviderImpl.swift 3 | // viper 4 | // 5 | // Created by Sergii Avilov on 1/17/17. 6 | // Copyright © 2017 test. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class InAppNotificationsProviderImpl { 12 | 13 | let msgTimeout = 3.0 14 | let errTimeout = 5.0 15 | 16 | init(withNavigation controller: UINavigationController) { 17 | navigation = controller 18 | } 19 | 20 | let navigation: UINavigationController 21 | } 22 | 23 | extension InAppNotificationsProviderImpl: InAppNotificationsProvider { 24 | 25 | func showError(err: Error) { 26 | showErrorMessage(msg: errorText(err: err)) 27 | } 28 | 29 | // 30 | 31 | func showErrorMessage(msg: String) { 32 | showMessage(title: "Error", msg: msg) 33 | } 34 | 35 | func showWarningMessage(msg: String) { 36 | showMessage(title: "Warning", msg: msg) 37 | } 38 | 39 | func showInfoMessage(msg: String) { 40 | showMessage(title: "Info", msg: msg) 41 | } 42 | 43 | } 44 | 45 | extension InAppNotificationsProviderImpl { 46 | 47 | func showMessage(title: String, msg: String) { 48 | let alert = UIAlertController(title: title, message: msg, preferredStyle: .alert) 49 | let alertAction = UIAlertAction(title: "OK", style: .cancel, handler: nil) 50 | alert.addAction(alertAction) 51 | navigation.present(alert, animated: true, completion: nil) 52 | } 53 | 54 | func errorText(err: Error) -> String { 55 | return err.localizedDescription 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /viper/Presentation/UserStories/DetailsScreen/DetailsScreenFactory.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DetailsScreenFactory.swift 3 | // viper 4 | // 5 | // Created by Sergii Avilov on 19/01/2017. 6 | // Copyright © 2017 Agilie. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | struct DetailsScreenFactory : ModuleFactoryProtocol { 12 | 13 | private static let _shared = DetailsScreenFactory() 14 | 15 | static var shared: DetailsScreenFactory { 16 | return _shared 17 | } 18 | 19 | // MARK: ModuleFactoryProtocol 20 | var moduleURN: String { 21 | return "DetailsScreen:{cityId}" 22 | } 23 | 24 | func createModule(arguments: NamedValuesType, completion: ModuleCompletionHandler?) -> UIViewController { 25 | NSLog("Opening module '\(storyboardId)' with controller '\(initialControllerID)'") 26 | 27 | let storyboard = UIStoryboard(name: storyboardId, bundle: nil) 28 | let controller = storyboard.instantiateViewController(withIdentifier: initialControllerID) 29 | 30 | let moduleInput = controller as? ModuleInputProtocol 31 | if let moduleInput = moduleInput { 32 | moduleInput.setupInitialState(withArguments: arguments, completion: completion) 33 | } else { 34 | fatalError("\(initialControllerID) moduleInput == nil") 35 | } 36 | 37 | return controller 38 | } 39 | 40 | // MARK: Helpers 41 | func createModuleURN(cityId: Int) -> String { 42 | return URNBuilder(string: moduleURN).buildWithArgs(args: ["\(cityId)"]) 43 | } 44 | 45 | // MARK: 46 | let storyboardId = "DetailsScreen" 47 | let initialControllerID = "DetailsScreenViewController" 48 | } 49 | -------------------------------------------------------------------------------- /viper/Presentation/UserStories/StartScreen/StartScreenFactory.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StartScreenFactory.swift 3 | // viper 4 | // 5 | // Created by Sergii Avilov on 18/01/2017. 6 | // Copyright © 2017 Agilie. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | //import CocoaLumberjack 11 | 12 | struct StartScreenFactory : ModuleFactoryProtocol { 13 | 14 | private static let _shared = StartScreenFactory() 15 | 16 | static var shared: StartScreenFactory { 17 | return _shared 18 | } 19 | 20 | // MARK: ModuleFactoryProtocol 21 | var moduleURN: String { 22 | return "StartScreen" 23 | } 24 | 25 | func createModule(arguments: NamedValuesType, completion: ModuleCompletionHandler?) -> UIViewController { 26 | NSLog("Opening module '\(storyboardId)' with controller '\(initialControllerID)'") 27 | 28 | let storyboard = UIStoryboard(name: storyboardId, bundle: nil) 29 | let controller = storyboard.instantiateViewController(withIdentifier: initialControllerID) 30 | 31 | let moduleInput = controller as? ModuleInputProtocol 32 | if let moduleInput = moduleInput { 33 | moduleInput.setupInitialState(withArguments: arguments, completion: completion) 34 | } else { 35 | fatalError("\(initialControllerID) moduleInput == nil") 36 | } 37 | 38 | return controller 39 | } 40 | 41 | // MARK: Helpers 42 | //func createModuleURN(arguments) -> String { 43 | // return URNBuilder(string: moduleURN).buildWithArgs(args: [arguments]) 44 | //} 45 | 46 | // MARK: 47 | let storyboardId = "StartScreen" 48 | let initialControllerID = "StartScreenViewController" 49 | } 50 | -------------------------------------------------------------------------------- /Pods/PromiseKit/Sources/AnyPromise+Private.h: -------------------------------------------------------------------------------- 1 | @import Foundation.NSError; 2 | @import Foundation.NSPointerArray; 3 | 4 | #if TARGET_OS_IPHONE 5 | #define NSPointerArrayMake(N) ({ \ 6 | NSPointerArray *aa = [NSPointerArray strongObjectsPointerArray]; \ 7 | aa.count = N; \ 8 | aa; \ 9 | }) 10 | #else 11 | static inline NSPointerArray * __nonnull NSPointerArrayMake(NSUInteger count) { 12 | #pragma clang diagnostic push 13 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" 14 | NSPointerArray *aa = [[NSPointerArray class] respondsToSelector:@selector(strongObjectsPointerArray)] 15 | ? [NSPointerArray strongObjectsPointerArray] 16 | : [NSPointerArray pointerArrayWithStrongObjects]; 17 | #pragma clang diagnostic pop 18 | aa.count = count; 19 | return aa; 20 | } 21 | #endif 22 | 23 | #define IsError(o) [o isKindOfClass:[NSError class]] 24 | #define IsPromise(o) [o isKindOfClass:[AnyPromise class]] 25 | 26 | #import "AnyPromise.h" 27 | 28 | @interface AnyPromise (Swift) 29 | - (AnyPromise * __nonnull)__thenOn:(__nonnull dispatch_queue_t)q execute:(id __nullable (^ __nonnull)(id __nullable))body; 30 | - (AnyPromise * __nonnull)__catchWithPolicy:(PMKCatchPolicy)policy execute:(id __nullable (^ __nonnull)(id __nullable))body; 31 | - (AnyPromise * __nonnull)__alwaysOn:(__nonnull dispatch_queue_t)q execute:(void (^ __nonnull)(void))body; 32 | - (void)__pipe:(void(^ __nonnull)(__nullable id))body; 33 | - (AnyPromise * __nonnull)initWithResolverBlock:(void (^ __nonnull)(PMKResolver __nonnull))resolver; 34 | @end 35 | 36 | extern NSError * __nullable PMKProcessUnhandledException(id __nonnull thrown); 37 | 38 | @class PMKArray; 39 | -------------------------------------------------------------------------------- /Pods/PromiseKit/Extensions/Foundation/Sources/NSTask+AnyPromise.h: -------------------------------------------------------------------------------- 1 | #if TARGET_OS_MAC && !TARGET_OS_EMBEDDED && !TARGET_OS_SIMULATOR 2 | 3 | #import 4 | #import 5 | 6 | #define PMKTaskErrorLaunchPathKey @"PMKTaskErrorLaunchPathKey" 7 | #define PMKTaskErrorArgumentsKey @"PMKTaskErrorArgumentsKey" 8 | #define PMKTaskErrorStandardOutputKey @"PMKTaskErrorStandardOutputKey" 9 | #define PMKTaskErrorStandardErrorKey @"PMKTaskErrorStandardErrorKey" 10 | #define PMKTaskErrorExitStatusKey @"PMKTaskErrorExitStatusKey" 11 | 12 | /** 13 | To import the `NSTask` category: 14 | 15 | use_frameworks! 16 | pod "PromiseKit/Foundation" 17 | 18 | Or `NSTask` is one of the categories imported by the umbrella pod: 19 | 20 | use_frameworks! 21 | pod "PromiseKit" 22 | 23 | And then in your sources: 24 | 25 | #import 26 | */ 27 | @interface NSTask (PromiseKit) 28 | 29 | /** 30 | Launches the receiver and resolves when it exits. 31 | 32 | If the task fails the promise is rejected with code `PMKTaskError`, and 33 | `userInfo` keys: `PMKTaskErrorStandardOutputKey`, 34 | `PMKTaskErrorStandardErrorKey` and `PMKTaskErrorExitStatusKey`. 35 | 36 | NSTask *task = [NSTask new]; 37 | task.launchPath = @"/usr/bin/basename"; 38 | task.arguments = @[@"/usr/bin/sleep"]; 39 | [task promise].then(^(NSString *stdout){ 40 | //… 41 | }); 42 | 43 | @return A promise that fulfills with three parameters: 44 | 45 | 1) The stdout interpreted as a UTF8 string. 46 | 2) The stderr interpreted as a UTF8 string. 47 | 3) The stdout as `NSData`. 48 | */ 49 | - (AnyPromise *)promise NS_REFINED_FOR_SWIFT; 50 | 51 | @end 52 | 53 | #endif 54 | -------------------------------------------------------------------------------- /Pods/ObjectMapper/Sources/ISO8601DateTransform.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ISO8601DateTransform.swift 3 | // ObjectMapper 4 | // 5 | // Created by Jean-Pierre Mouilleseaux on 21 Nov 2014. 6 | // 7 | // The MIT License (MIT) 8 | // 9 | // Copyright (c) 2014-2016 Hearst 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining a copy 12 | // of this software and associated documentation files (the "Software"), to deal 13 | // in the Software without restriction, including without limitation the rights 14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | // copies of the Software, and to permit persons to whom the Software is 16 | // furnished to do so, subject to the following conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be included in 19 | // all copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | // THE SOFTWARE. 28 | 29 | import Foundation 30 | 31 | open class ISO8601DateTransform: DateFormatterTransform { 32 | 33 | public init() { 34 | let formatter = DateFormatter() 35 | formatter.locale = Locale(identifier: "en_US_POSIX") 36 | formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ" 37 | 38 | super.init(dateFormatter: formatter) 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /Pods/ObjectMapper/Sources/CustomDateFormatTransform.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CustomDateFormatTransform.swift 3 | // ObjectMapper 4 | // 5 | // Created by Dan McCracken on 3/8/15. 6 | // 7 | // The MIT License (MIT) 8 | // 9 | // Copyright (c) 2014-2016 Hearst 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining a copy 12 | // of this software and associated documentation files (the "Software"), to deal 13 | // in the Software without restriction, including without limitation the rights 14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | // copies of the Software, and to permit persons to whom the Software is 16 | // furnished to do so, subject to the following conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be included in 19 | // all copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | // THE SOFTWARE. 28 | 29 | import Foundation 30 | 31 | open class CustomDateFormatTransform: DateFormatterTransform { 32 | 33 | public init(formatString: String) { 34 | let formatter = DateFormatter() 35 | formatter.locale = Locale(identifier: "en_US_POSIX") 36 | formatter.dateFormat = formatString 37 | 38 | super.init(dateFormatter: formatter) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /viper/Services/ApiService/RequestExecutor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RequestExecutor.swift 3 | // viper 4 | // 5 | // Created by Sergii Avilov on 4/11/17. 6 | // Copyright © 2017 test. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Alamofire 11 | import PromiseKit 12 | 13 | class RequestExecutor { 14 | 15 | // MARK: 16 | fileprivate let builder: RequestBuilder 17 | fileprivate let configuration: URLSessionConfiguration = URLSessionConfiguration.default 18 | fileprivate let queue: DispatchQueue 19 | fileprivate let session: SessionManager 20 | 21 | //MARK: 22 | init(requestBuilder: RequestBuilder) { 23 | queue = DispatchQueue.global(qos: .utility) 24 | builder = requestBuilder 25 | session = SessionManager(configuration: configuration) 26 | } 27 | 28 | // MARK: 29 | fileprivate func processResponse(response: DataResponse, checkToken: Bool, completion: @escaping (Any?, APIError?) -> Void) { 30 | NSLog("response: %@", response.debugDescription) 31 | let statusCode = response.response?.statusCode ?? 500 32 | if statusCode == 200 { 33 | completion(response.result.value, nil) 34 | } else { 35 | completion(nil, APIError(message: "Server error")) 36 | } 37 | } 38 | 39 | //MARK: 40 | func runRequest(request: URLRequest, completion: @escaping (Any?, APIError?) -> Void) { 41 | NSLog("request: %@", request.debugDescription) 42 | session.request(request) 43 | .validate(statusCode: [200]) 44 | .responseJSON(queue: queue) { (response) in 45 | self.processResponse(response: response, checkToken: true, completion: completion) 46 | } 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /Pods/PromiseKit/Extensions/UIKit/Sources/UIView+Promise.swift: -------------------------------------------------------------------------------- 1 | import UIKit.UIView 2 | #if !COCOAPODS 3 | import PromiseKit 4 | #endif 5 | 6 | /** 7 | To import the `UIView` category: 8 | 9 | use_frameworks! 10 | pod "PromiseKit/UIKit" 11 | 12 | Or `UIKit` is one of the categories imported by the umbrella pod: 13 | 14 | use_frameworks! 15 | pod "PromiseKit" 16 | 17 | And then in your sources: 18 | 19 | import PromiseKit 20 | */ 21 | extension UIView { 22 | /** 23 | Animate changes to one or more views using the specified duration, delay, 24 | options, and completion handler. 25 | 26 | - Parameter duration: The total duration of the animations, measured in 27 | seconds. If you specify a negative value or 0, the changes are made 28 | without animating them. 29 | 30 | - Parameter delay: The amount of time (measured in seconds) to wait before 31 | beginning the animations. Specify a value of 0 to begin the animations 32 | immediately. 33 | 34 | - Parameter options: A mask of options indicating how you want to perform the 35 | animations. For a list of valid constants, see UIViewAnimationOptions. 36 | 37 | - Parameter animations: A block object containing the changes to commit to the 38 | views. 39 | 40 | - Returns: A promise that fulfills with a boolean NSNumber indicating 41 | whether or not the animations actually finished. 42 | */ 43 | public class func promise(animateWithDuration duration: TimeInterval, delay: TimeInterval = 0, options: UIViewAnimationOptions = [], animations: @escaping () -> Void) -> Promise { 44 | return PromiseKit.wrap { animate(withDuration: duration, delay: delay, options: options, animations: animations, completion: $0) } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /viper/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 | -------------------------------------------------------------------------------- /viper/Presentation/Routing/AppRouterProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppRouterProtocol.swift 3 | // viper 4 | // 5 | // Created by Sergii Avilov on 1/17/17. 6 | // Copyright © 2017 test. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol AppRouterProtocol { 12 | 13 | var navigationController: UINavigationController { get } 14 | 15 | /// Push module into navigation stack 16 | /// 17 | /// - Parameters: 18 | /// - urn: module urn 19 | /// - animated: 20 | /// - completion: module completion handler 21 | func pushModule(byUrn urn: String, animated: Bool, completion: ModuleCompletionHandler?) 22 | 23 | /// Set module as navigation stack root (and clear current) 24 | /// 25 | /// - Parameters: 26 | /// - urn: module urn 27 | /// - animated: 28 | /// - completion: module completion handler 29 | func replaceViewStack(rootUrn urn: String, animated: Bool, completion: ModuleCompletionHandler?) 30 | 31 | /// Present module on navigation stack 32 | /// 33 | /// - Parameters: 34 | /// - urn: module urn 35 | /// - animated: 36 | /// - completion: module completion handler 37 | func presentModule(byUrn urn: String, animated: Bool, completion: ModuleCompletionHandler?) 38 | 39 | /// Pop modules from navigation stack to view controller (module) 40 | /// 41 | /// - Parameters: 42 | /// - controller: 43 | /// - animated: 44 | func popToViewController(_ controller: UIViewController, animated: Bool) 45 | 46 | 47 | /// Pop top controller from navigation stack 48 | /// 49 | /// - Parameter animated: 50 | func popCurrentController(animated: Bool) 51 | 52 | /// Dismiss controller from navigation stack 53 | /// 54 | /// - Parameter animated: 55 | func dismissCurrentController(animated: Bool) 56 | } 57 | -------------------------------------------------------------------------------- /Pods/ObjectMapper/Sources/EnumTransform.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EnumTransform.swift 3 | // ObjectMapper 4 | // 5 | // Created by Kaan Dedeoglu on 3/20/15. 6 | // 7 | // The MIT License (MIT) 8 | // 9 | // Copyright (c) 2014-2016 Hearst 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining a copy 12 | // of this software and associated documentation files (the "Software"), to deal 13 | // in the Software without restriction, including without limitation the rights 14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | // copies of the Software, and to permit persons to whom the Software is 16 | // furnished to do so, subject to the following conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be included in 19 | // all copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | // THE SOFTWARE. 28 | 29 | import Foundation 30 | 31 | open class EnumTransform: TransformType { 32 | public typealias Object = T 33 | public typealias JSON = T.RawValue 34 | 35 | public init() {} 36 | 37 | open func transformFromJSON(_ value: Any?) -> T? { 38 | if let raw = value as? T.RawValue { 39 | return T(rawValue: raw) 40 | } 41 | return nil 42 | } 43 | 44 | open func transformToJSON(_ value: T?) -> T.RawValue? { 45 | if let obj = value { 46 | return obj.rawValue 47 | } 48 | return nil 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Pods/ObjectMapper/Sources/DataTransform.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DataTransform.swift 3 | // ObjectMapper 4 | // 5 | // Created by Yagrushkin, Evgeny on 8/30/16. 6 | // 7 | // The MIT License (MIT) 8 | // 9 | // Copyright (c) 2014-2016 Hearst 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining a copy 12 | // of this software and associated documentation files (the "Software"), to deal 13 | // in the Software without restriction, including without limitation the rights 14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | // copies of the Software, and to permit persons to whom the Software is 16 | // furnished to do so, subject to the following conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be included in 19 | // all copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | // THE SOFTWARE. 28 | 29 | import Foundation 30 | 31 | open class DataTransform: TransformType { 32 | public typealias Object = Data 33 | public typealias JSON = String 34 | 35 | public init() {} 36 | 37 | open func transformFromJSON(_ value: Any?) -> Data? { 38 | guard let string = value as? String else{ 39 | return nil 40 | } 41 | return Data(base64Encoded: string) 42 | } 43 | 44 | open func transformToJSON(_ value: Data?) -> String? { 45 | guard let data = value else{ 46 | return nil 47 | } 48 | return data.base64EncodedString() 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Pods/PromiseKit/Sources/DispatchQueue+Promise.swift: -------------------------------------------------------------------------------- 1 | import Dispatch 2 | 3 | extension DispatchQueue { 4 | /** 5 | Submits a block for asynchronous execution on a dispatch queue. 6 | 7 | DispatchQueue.global().promise { 8 | try md5(input) 9 | }.then { md5 in 10 | //… 11 | } 12 | 13 | - Parameter body: The closure that resolves this promise. 14 | - Returns: A new promise resolved by the result of the provided closure. 15 | 16 | - SeeAlso: `DispatchQueue.async(group:qos:flags:execute:)` 17 | - SeeAlso: `dispatch_promise()` 18 | - SeeAlso: `dispatch_promise_on()` 19 | */ 20 | public final func promise(group: DispatchGroup? = nil, qos: DispatchQoS = .default, flags: DispatchWorkItemFlags = [], execute body: @escaping () throws -> T) -> Promise { 21 | 22 | return Promise(sealant: { resolve in 23 | async(group: group, qos: qos, flags: flags) { 24 | do { 25 | resolve(.fulfilled(try body())) 26 | } catch { 27 | resolve(Resolution(error)) 28 | } 29 | } 30 | }) 31 | } 32 | 33 | /// Unavailable due to Swift compiler issues 34 | @available(*, unavailable) 35 | public final func promise(group: DispatchGroup? = nil, qos: DispatchQoS = .default, flags: DispatchWorkItemFlags = [], execute body: () throws -> Promise) -> Promise { fatalError() } 36 | 37 | /** 38 | The default queue for all handlers. 39 | 40 | Defaults to `DispatchQueue.main`. 41 | 42 | - SeeAlso: `PMKDefaultDispatchQueue()` 43 | - SeeAlso: `PMKSetDefaultDispatchQueue()` 44 | */ 45 | class public final var `default`: DispatchQueue { 46 | get { 47 | return __PMKDefaultDispatchQueue() 48 | } 49 | set { 50 | __PMKSetDefaultDispatchQueue(newValue) 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/DispatchQueue+Alamofire.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DispatchQueue+Alamofire.swift 3 | // 4 | // Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Dispatch 26 | import Foundation 27 | 28 | extension DispatchQueue { 29 | static var userInteractive: DispatchQueue { return DispatchQueue.global(qos: .userInteractive) } 30 | static var userInitiated: DispatchQueue { return DispatchQueue.global(qos: .userInitiated) } 31 | static var utility: DispatchQueue { return DispatchQueue.global(qos: .utility) } 32 | static var background: DispatchQueue { return DispatchQueue.global(qos: .background) } 33 | 34 | func after(_ delay: TimeInterval, execute closure: @escaping () -> Void) { 35 | asyncAfter(deadline: .now() + delay, execute: closure) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Pods/ObjectMapper/Sources/TransformOf.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TransformOf.swift 3 | // ObjectMapper 4 | // 5 | // Created by Syo Ikeda on 1/23/15. 6 | // 7 | // The MIT License (MIT) 8 | // 9 | // Copyright (c) 2014-2016 Hearst 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining a copy 12 | // of this software and associated documentation files (the "Software"), to deal 13 | // in the Software without restriction, including without limitation the rights 14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | // copies of the Software, and to permit persons to whom the Software is 16 | // furnished to do so, subject to the following conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be included in 19 | // all copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | // THE SOFTWARE. 28 | 29 | open class TransformOf: TransformType { 30 | public typealias Object = ObjectType 31 | public typealias JSON = JSONType 32 | 33 | private let fromJSON: (JSONType?) -> ObjectType? 34 | private let toJSON: (ObjectType?) -> JSONType? 35 | 36 | public init(fromJSON: @escaping(JSONType?) -> ObjectType?, toJSON: @escaping(ObjectType?) -> JSONType?) { 37 | self.fromJSON = fromJSON 38 | self.toJSON = toJSON 39 | } 40 | 41 | open func transformFromJSON(_ value: Any?) -> ObjectType? { 42 | return fromJSON(value as? JSONType) 43 | } 44 | 45 | open func transformToJSON(_ value: ObjectType?) -> JSONType? { 46 | return toJSON(value) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Pods/ObjectMapper/Sources/DateTransform.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DateTransform.swift 3 | // ObjectMapper 4 | // 5 | // Created by Tristan Himmelman on 2014-10-13. 6 | // 7 | // The MIT License (MIT) 8 | // 9 | // Copyright (c) 2014-2016 Hearst 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining a copy 12 | // of this software and associated documentation files (the "Software"), to deal 13 | // in the Software without restriction, including without limitation the rights 14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | // copies of the Software, and to permit persons to whom the Software is 16 | // furnished to do so, subject to the following conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be included in 19 | // all copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | // THE SOFTWARE. 28 | 29 | import Foundation 30 | 31 | open class DateTransform: TransformType { 32 | public typealias Object = Date 33 | public typealias JSON = Double 34 | 35 | public init() {} 36 | 37 | open func transformFromJSON(_ value: Any?) -> Date? { 38 | if let timeInt = value as? Double { 39 | return Date(timeIntervalSince1970: TimeInterval(timeInt)) 40 | } 41 | 42 | if let timeStr = value as? String { 43 | return Date(timeIntervalSince1970: TimeInterval(atof(timeStr))) 44 | } 45 | 46 | return nil 47 | } 48 | 49 | open func transformToJSON(_ value: Date?) -> Double? { 50 | if let date = value { 51 | return Double(date.timeIntervalSince1970) 52 | } 53 | return nil 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Pods/ObjectMapper/Sources/DateFormatterTransform.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DateFormatterTransform.swift 3 | // ObjectMapper 4 | // 5 | // Created by Tristan Himmelman on 2015-03-09. 6 | // 7 | // The MIT License (MIT) 8 | // 9 | // Copyright (c) 2014-2016 Hearst 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining a copy 12 | // of this software and associated documentation files (the "Software"), to deal 13 | // in the Software without restriction, including without limitation the rights 14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | // copies of the Software, and to permit persons to whom the Software is 16 | // furnished to do so, subject to the following conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be included in 19 | // all copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | // THE SOFTWARE. 28 | 29 | import Foundation 30 | 31 | open class DateFormatterTransform: TransformType { 32 | public typealias Object = Date 33 | public typealias JSON = String 34 | 35 | public let dateFormatter: DateFormatter 36 | 37 | public init(dateFormatter: DateFormatter) { 38 | self.dateFormatter = dateFormatter 39 | } 40 | 41 | open func transformFromJSON(_ value: Any?) -> Date? { 42 | if let dateString = value as? String { 43 | return dateFormatter.date(from: dateString) 44 | } 45 | return nil 46 | } 47 | 48 | open func transformToJSON(_ value: Date?) -> String? { 49 | if let date = value { 50 | return dateFormatter.string(from: date) 51 | } 52 | return nil 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Pods/PromiseKit/Extensions/Foundation/Sources/NSTask+AnyPromise.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import 4 | #import 5 | #import 6 | 7 | #if TARGET_OS_MAC && !TARGET_OS_EMBEDDED && !TARGET_OS_SIMULATOR 8 | 9 | #import "NSTask+AnyPromise.h" 10 | 11 | @implementation NSTask (PromiseKit) 12 | 13 | - (AnyPromise *)promise { 14 | return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) { 15 | self.standardOutput = [NSPipe pipe]; 16 | self.standardError = [NSPipe pipe]; 17 | self.terminationHandler = ^(NSTask *task){ 18 | id stdoutData = [[task.standardOutput fileHandleForReading] readDataToEndOfFile]; 19 | id stdoutString = [[NSString alloc] initWithData:stdoutData encoding:NSUTF8StringEncoding]; 20 | id stderrData = [[task.standardError fileHandleForReading] readDataToEndOfFile]; 21 | id stderrString = [[NSString alloc] initWithData:stderrData encoding:NSUTF8StringEncoding]; 22 | 23 | if (task.terminationReason == NSTaskTerminationReasonExit && self.terminationStatus == 0) { 24 | resolve(PMKManifold(stdoutString, stderrString, stdoutData)); 25 | } else { 26 | id cmd = [NSMutableArray arrayWithObject:task.launchPath]; 27 | [cmd addObjectsFromArray:task.arguments]; 28 | cmd = [cmd componentsJoinedByString:@" "]; 29 | 30 | id info = @{ 31 | NSLocalizedDescriptionKey:[NSString stringWithFormat:@"Failed executing: %@.", cmd], 32 | PMKTaskErrorStandardOutputKey: stdoutString, 33 | PMKTaskErrorStandardErrorKey: stderrString, 34 | PMKTaskErrorExitStatusKey: @(task.terminationStatus), 35 | }; 36 | 37 | resolve([NSError errorWithDomain:PMKErrorDomain code:PMKTaskError userInfo:info]); 38 | } 39 | }; 40 | [self launch]; 41 | }]; 42 | } 43 | 44 | @end 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /Pods/ObjectMapper/Sources/NSDecimalNumberTransform.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TransformOf.swift 3 | // ObjectMapper 4 | // 5 | // Created by Tristan Himmelman on 8/22/16. 6 | // 7 | // The MIT License (MIT) 8 | // 9 | // Copyright (c) 2014-2016 Hearst 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining a copy 12 | // of this software and associated documentation files (the "Software"), to deal 13 | // in the Software without restriction, including without limitation the rights 14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | // copies of the Software, and to permit persons to whom the Software is 16 | // furnished to do so, subject to the following conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be included in 19 | // all copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | // THE SOFTWARE. 28 | 29 | import Foundation 30 | 31 | open class NSDecimalNumberTransform: TransformType { 32 | public typealias Object = NSDecimalNumber 33 | public typealias JSON = String 34 | 35 | public init() {} 36 | 37 | open func transformFromJSON(_ value: Any?) -> NSDecimalNumber? { 38 | if let string = value as? String { 39 | return NSDecimalNumber(string: string) 40 | } 41 | if let double = value as? Double { 42 | return NSDecimalNumber(floatLiteral: double) 43 | } 44 | return nil 45 | } 46 | 47 | open func transformToJSON(_ value: NSDecimalNumber?) -> String? { 48 | guard let value = value else { return nil } 49 | return value.description 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /viper/Presentation/UserStories/DetailsScreen/Presenter/DetailsScreenPresenter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DetailsScreenPresenter.swift 3 | // viper 4 | // 5 | // Created by Sergii Avilov on 19/01/2017. 6 | // Copyright © 2017 Agilie. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import PromiseKit 11 | 12 | class DetailsScreenPresenter { 13 | 14 | weak var view: DetailsScreenViewInput! 15 | var interactor: DetailsScreenInteractorInput! 16 | var moduleCompletion: ModuleCompletionHandler? 17 | } 18 | 19 | // MARK: 20 | extension DetailsScreenPresenter: DetailsScreenViewOutput { 21 | 22 | func setupInitialState(withArguments args: NamedValuesType, completion: ModuleCompletionHandler?) { 23 | moduleCompletion = completion 24 | } 25 | 26 | func viewIsReady() { 27 | 28 | } 29 | 30 | func city(with id: Int) -> City? { 31 | return interactor.city(with: id) 32 | } 33 | 34 | func requestWeather(for cityId: Int) { 35 | //show progress indicator if needed 36 | interactor.obtainCurrentWeather(for: cityId) 37 | .then { (weather) -> Void in 38 | if self.view != nil { 39 | self.view.assignWeather(weather) 40 | } 41 | } 42 | .catch { (error) in 43 | if let viewController = self.view as? UIViewController { 44 | self.showError(error, for: viewController) 45 | } 46 | } 47 | .always { 48 | //hide progress indicator if shown 49 | } 50 | } 51 | 52 | } 53 | 54 | extension DetailsScreenPresenter { 55 | 56 | func showError(_ error: Error, for controller: UIViewController) { 57 | var errorMessage = error.localizedDescription 58 | if let apiError = error as? APIError { 59 | errorMessage = apiError.message 60 | } 61 | let alert = UIAlertController.init(title: "Error", message: errorMessage, preferredStyle: .alert) 62 | let okAction = UIAlertAction(title: "OK", style: .cancel, handler: nil) 63 | alert.addAction(okAction) 64 | controller.present(alert, animated: true, completion: nil) 65 | } 66 | 67 | } 68 | 69 | -------------------------------------------------------------------------------- /viper/Presentation/UserStories/DetailsScreen/View/DetailsScreenViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DetailsScreenViewController.swift 3 | // viper 4 | // 5 | // Created by Sergii Avilov on 19/01/2017. 6 | // Copyright © 2017 Agilie. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class DetailsScreenViewController: UIViewController { 12 | 13 | // MARK: 14 | var output: DetailsScreenViewOutput! 15 | var cityId: Int = 0 16 | 17 | // MARK: Life cycle 18 | override func viewDidLoad() { 19 | super.viewDidLoad() 20 | title = "Details Screen" 21 | if let city = output.city(with: cityId) { 22 | title = city.title 23 | } 24 | output.viewIsReady() 25 | output.requestWeather(for: cityId) 26 | } 27 | 28 | // MARK: Handlers 29 | 30 | // MARK: Outlets 31 | @IBOutlet weak var temperatureLabel: UILabel! 32 | @IBOutlet weak var humidityLabel: UILabel! 33 | @IBOutlet weak var pressureLabel: UILabel! 34 | @IBOutlet weak var windLabel: UILabel! 35 | @IBOutlet weak var cloundsLabel: UILabel! 36 | @IBOutlet weak var visibilityLabel: UILabel! 37 | 38 | } 39 | 40 | // MARK: 41 | extension DetailsScreenViewController: ModuleInputProtocol { 42 | 43 | func setupInitialState(withArguments args: NamedValuesType, completion: ModuleCompletionHandler?) { 44 | cityId = Int(args["cityId"] as! String)! 45 | output.setupInitialState(withArguments: args, completion: completion) 46 | } 47 | } 48 | 49 | // MARK: 50 | extension DetailsScreenViewController: DetailsScreenViewInput { 51 | 52 | func assignWeather(_ weather: CurrentWeather) { 53 | reloadUI(with: weather) 54 | } 55 | 56 | } 57 | 58 | extension DetailsScreenViewController { 59 | 60 | func reloadUI(with weather: CurrentWeather) { 61 | temperatureLabel.text = weather.temperatureString 62 | humidityLabel.text = weather.humidityString 63 | pressureLabel.text = weather.pressureString 64 | temperatureLabel.text = weather.temperatureString 65 | windLabel.text = weather.windString 66 | cloundsLabel.text = weather.cloudsString 67 | visibilityLabel.text = weather.visibilityString 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /Pods/PromiseKit/Extensions/Foundation/Sources/NSObject+Promise.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | #if !COCOAPODS 3 | import PromiseKit 4 | #endif 5 | 6 | /** 7 | To import the `NSObject` category: 8 | 9 | use_frameworks! 10 | pod "PromiseKit/Foundation" 11 | 12 | Or `NSObject` is one of the categories imported by the umbrella pod: 13 | 14 | use_frameworks! 15 | pod "PromiseKit" 16 | 17 | And then in your sources: 18 | 19 | import PromiseKit 20 | */ 21 | extension NSObject { 22 | /** 23 | - Returns: A promise that resolves when the provided keyPath changes. 24 | - Warning: *Important* The promise must not outlive the object under observation. 25 | - SeeAlso: Apple’s KVO documentation. 26 | */ 27 | public func observe(keyPath: String) -> Promise { 28 | let (promise, fulfill, reject) = Promise.pending() 29 | let proxy = KVOProxy(observee: self, keyPath: keyPath) { obj in 30 | if let obj = obj as? T { 31 | fulfill(obj) 32 | } else { 33 | reject(PMKError.castError(T.self)) 34 | } 35 | } 36 | proxy.retainCycle = proxy 37 | return promise 38 | } 39 | } 40 | 41 | private class KVOProxy: NSObject { 42 | var retainCycle: KVOProxy? 43 | let fulfill: (Any?) -> Void 44 | 45 | init(observee: NSObject, keyPath: String, resolve: @escaping (Any?) -> Void) { 46 | fulfill = resolve 47 | super.init() 48 | observee.addObserver(self, forKeyPath: keyPath, options: NSKeyValueObservingOptions.new, context: pointer) 49 | } 50 | 51 | fileprivate override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { 52 | if let change = change, context == pointer { 53 | defer { retainCycle = nil } 54 | fulfill(change[NSKeyValueChangeKey.newKey]) 55 | if let object = object as? NSObject, let keyPath = keyPath { 56 | object.removeObserver(self, forKeyPath: keyPath) 57 | } 58 | } 59 | } 60 | 61 | private lazy var pointer: UnsafeMutableRawPointer = { 62 | return Unmanaged.passUnretained(self).toOpaque() 63 | }() 64 | } 65 | -------------------------------------------------------------------------------- /Pods/PromiseKit/Sources/join.m: -------------------------------------------------------------------------------- 1 | @import Foundation.NSDictionary; 2 | #import "AnyPromise+Private.h" 3 | #import 4 | @import Foundation.NSError; 5 | @import Foundation.NSNull; 6 | #import "PromiseKit.h" 7 | #import 8 | 9 | /** 10 | Waits on all provided promises. 11 | 12 | `PMKWhen` rejects as soon as one of the provided promises rejects. `PMKJoin` waits on all provided promises, then rejects if any of those promises rejects, otherwise it fulfills with values from the provided promises. 13 | 14 | - Returns: A new promise that resolves once all the provided promises resolve. 15 | */ 16 | AnyPromise *PMKJoin(NSArray *promises) { 17 | if (promises == nil) 18 | return [AnyPromise promiseWithValue:[NSError errorWithDomain:PMKErrorDomain code:PMKInvalidUsageError userInfo:@{NSLocalizedDescriptionKey: @"PMKJoin(nil)"}]]; 19 | 20 | if (promises.count == 0) 21 | return [AnyPromise promiseWithValue:promises]; 22 | 23 | return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) { 24 | NSPointerArray *results = NSPointerArrayMake(promises.count); 25 | __block atomic_int countdown = promises.count; 26 | __block BOOL rejected = NO; 27 | 28 | [promises enumerateObjectsUsingBlock:^(AnyPromise *promise, NSUInteger ii, BOOL *stop) { 29 | [promise __pipe:^(id value) { 30 | 31 | if (IsError(value)) { 32 | rejected = YES; 33 | } 34 | 35 | //FIXME surely this isn't thread safe on multiple cores? 36 | [results replacePointerAtIndex:ii withPointer:(__bridge void *)(value ?: [NSNull null])]; 37 | 38 | atomic_fetch_sub_explicit(&countdown, 1, memory_order_relaxed); 39 | 40 | if (countdown == 0) { 41 | if (!rejected) { 42 | resolve(results.allObjects); 43 | } else { 44 | id userInfo = @{PMKJoinPromisesKey: promises}; 45 | id err = [NSError errorWithDomain:PMKErrorDomain code:PMKJoinError userInfo:userInfo]; 46 | resolve(err); 47 | } 48 | } 49 | }]; 50 | 51 | (void) stop; 52 | }]; 53 | }]; 54 | } 55 | -------------------------------------------------------------------------------- /viper/Model/CurrentWeather.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CurrentWhether.swift 3 | // viper 4 | // 5 | // Created by Sergii Avilov on 4/11/17. 6 | // Copyright © 2017 test. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import ObjectMapper 11 | 12 | class CurrentWeather: Mappable { 13 | 14 | var clouds: Float? 15 | var humidity: Float? 16 | var pressure: Float? 17 | var temperature: Float? 18 | var city: String? 19 | var windDegree: Float? 20 | var windSpeed: Float? 21 | var visibility: Float? 22 | 23 | required init?(map: Map) { 24 | mapping(map: map) 25 | } 26 | 27 | func mapping(map: Map) { 28 | clouds <- map["clouds.all"] 29 | humidity <- map["main.humidity"] 30 | pressure <- map["main.pressure"] 31 | temperature <- map["main.temp"] 32 | city <- map["name"] 33 | windDegree <- map["wind.deg"] 34 | windSpeed <- map["wind.speed"] 35 | visibility <- map["visibility"] 36 | } 37 | 38 | var cloudsString: String { 39 | let valueString = stringValue(for: clouds) 40 | return "Clouds: \(valueString)%" 41 | } 42 | 43 | var humidityString: String { 44 | let valueString = stringValue(for: humidity) 45 | return "Humidity: \(valueString)%" 46 | } 47 | 48 | var pressureString: String { 49 | let valueString = stringValue(for: pressure) 50 | return "Pressure: \(valueString) hPa" 51 | } 52 | 53 | var temperatureString: String { 54 | var valueString = "-" 55 | if let temp = temperature { 56 | valueString = "\(Int(temp - 273.15))" 57 | } 58 | return "\(valueString)º" 59 | } 60 | 61 | var windString: String { 62 | let valueStringDegree = stringValue(for: windDegree) 63 | let valueStringSpeed = stringValue(for: windSpeed) 64 | return "Wind: direction \(valueStringDegree)º, speed \(valueStringSpeed)m/s" 65 | } 66 | 67 | var visibilityString: String { 68 | let valueString = stringValue(for: visibility) 69 | return "Visibility: \(valueString) meters" 70 | } 71 | 72 | } 73 | 74 | extension CurrentWeather { 75 | 76 | func stringValue(for value: Any?) -> String { 77 | return value != nil ? "\(value!)" : "-" 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /Pods/PromiseKit/Extensions/UIKit/Sources/UIViewController+AnyPromise.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | /** 5 | To import the `UIViewController` category: 6 | 7 | use_frameworks! 8 | pod "PromiseKit/UIKit" 9 | 10 | Or `UIKit` is one of the categories imported by the umbrella pod: 11 | 12 | use_frameworks! 13 | pod "PromiseKit" 14 | 15 | And then in your sources: 16 | 17 | @import PromiseKit; 18 | */ 19 | @interface UIViewController (PromiseKit) 20 | 21 | /** 22 | Presents a view controller modally. 23 | 24 | If the view controller is one of the following: 25 | 26 | - MFMailComposeViewController 27 | - MFMessageComposeViewController 28 | - UIImagePickerController 29 | - SLComposeViewController 30 | 31 | Then PromiseKit presents the view controller returning a promise that is 32 | resolved as per the documentation for those classes. Eg. if you present a 33 | `UIImagePickerController` the view controller will be presented for you 34 | and the returned promise will resolve with the media the user selected. 35 | 36 | [self promiseViewController:[MFMailComposeViewController new] animated:YES completion:nil].then(^{ 37 | //… 38 | }); 39 | 40 | Otherwise PromiseKit expects your view controller to implement a 41 | `promise` property. This promise will be returned from this method and 42 | presentation and dismissal of the presented view controller will be 43 | managed for you. 44 | 45 | \@interface MyViewController: UIViewController 46 | @property (readonly) AnyPromise *promise; 47 | @end 48 | 49 | @implementation MyViewController { 50 | PMKResolver resolve; 51 | } 52 | 53 | - (void)viewDidLoad { 54 | _promise = [[AnyPromise alloc] initWithResolver:&resolve]; 55 | } 56 | 57 | - (void)later { 58 | resolve(@"some fulfilled value"); 59 | } 60 | 61 | @end 62 | 63 | [self promiseViewController:[MyViewController new] aniamted:YES completion:nil].then(^(id value){ 64 | // value == @"some fulfilled value" 65 | }); 66 | 67 | @return A promise that can be resolved by the presented view controller. 68 | */ 69 | - (AnyPromise *)promiseViewController:(UIViewController *)vc animated:(BOOL)animated completion:(void (^)(void))block NS_REFINED_FOR_SWIFT; 70 | 71 | @end 72 | -------------------------------------------------------------------------------- /Pods/PromiseKit/Sources/join.swift: -------------------------------------------------------------------------------- 1 | import Dispatch 2 | 3 | /** 4 | Waits on all provided promises. 5 | 6 | `when` rejects as soon as one of the provided promises rejects. `join` waits on all provided promises, then rejects if any of those promises rejected, otherwise it fulfills with values from the provided promises. 7 | 8 | join(promise1, promise2, promise3).then { results in 9 | //… 10 | }.catch { error in 11 | switch error { 12 | case Error.Join(let promises): 13 | //… 14 | } 15 | } 16 | 17 | - Returns: A new promise that resolves once all the provided promises resolve. 18 | - SeeAlso: `PromiseKit.Error.join` 19 | */ 20 | @available(*, deprecated: 4.0, message: "Use when(resolved:)") 21 | public func join(_ promises: Promise...) -> Promise<[T]> { 22 | return join(promises) 23 | } 24 | 25 | /// Waits on all provided promises. 26 | @available(*, deprecated: 4.0, message: "Use when(resolved:)") 27 | public func join(_ promises: [Promise]) -> Promise { 28 | return join(promises).then(on: zalgo) { (_: [Void]) in return Promise(value: ()) } 29 | } 30 | 31 | /// Waits on all provided promises. 32 | @available(*, deprecated: 4.0, message: "Use when(resolved:)") 33 | public func join(_ promises: [Promise]) -> Promise<[T]> { 34 | guard !promises.isEmpty else { return Promise(value: []) } 35 | 36 | var countdown = promises.count 37 | let barrier = DispatchQueue(label: "org.promisekit.barrier.join", attributes: .concurrent) 38 | var rejected = false 39 | 40 | return Promise { fulfill, reject in 41 | for promise in promises { 42 | promise.state.pipe { resolution in 43 | barrier.sync(flags: .barrier) { 44 | if case .rejected(_, let token) = resolution { 45 | token.consumed = true // the parent Error.Join consumes all 46 | rejected = true 47 | } 48 | countdown -= 1 49 | if countdown == 0 { 50 | if rejected { 51 | reject(PMKError.join(promises)) 52 | } else { 53 | fulfill(promises.map{ $0.value! }) 54 | } 55 | } 56 | } 57 | } 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Pods/PromiseKit/Extensions/Foundation/Sources/NSURLSession+AnyPromise.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import 4 | #import 5 | 6 | #define PMKURLErrorFailingURLResponseKey @"PMKURLErrorFailingURLResponseKey" 7 | #define PMKURLErrorFailingDataKey @"PMKURLErrorFailingDataKey" 8 | #define PMKURLErrorFailingStringKey @"PMKURLErrorFailingStringKey" 9 | #define PMKJSONErrorJSONObjectKey @"PMKJSONErrorJSONObjectKey" 10 | 11 | /** 12 | To import the `NSURLSession` category: 13 | 14 | use_frameworks! 15 | pod "PromiseKit/Foundation" 16 | 17 | Or `NSURLConnection` is one of the categories imported by the umbrella pod: 18 | 19 | use_frameworks! 20 | pod "PromiseKit" 21 | 22 | And then in your sources: 23 | 24 | #import 25 | */ 26 | @interface NSURLSession (PromiseKit) 27 | 28 | /** 29 | Creates a task that retrieves the contents of a URL based on the 30 | specified URL request object. 31 | 32 | PromiseKit automatically deserializes the raw HTTP data response into the 33 | appropriate rich data type based on the mime type the server provides. 34 | Thus if the response is JSON you will get the deserialized JSON response. 35 | PromiseKit supports decoding into strings, JSON and UIImages. 36 | 37 | However if your server does not provide a rich content-type, you will 38 | just get `NSData`. This is rare, but a good example we came across was 39 | downloading files from Dropbox. 40 | 41 | PromiseKit goes to quite some lengths to provide good `NSError` objects 42 | for error conditions at all stages of the HTTP to rich-data type 43 | pipeline. We provide the following additional `userInfo` keys as 44 | appropriate: 45 | 46 | - `PMKURLErrorFailingDataKey` 47 | - `PMKURLErrorFailingStringKey` 48 | - `PMKURLErrorFailingURLResponseKey` 49 | 50 | [[NSURLConnection sharedSession] promiseDataTaskWithRequest:rq].then(^(id response){ 51 | // response is probably an NSDictionary deserialized from JSON 52 | }); 53 | 54 | @param request The URL request. 55 | 56 | @return A promise that fulfills with three parameters: 57 | 58 | 1) The deserialized data response. 59 | 2) The `NSHTTPURLResponse`. 60 | 3) The raw `NSData` response. 61 | 62 | @see https://github.com/mxcl/OMGHTTPURLRQ 63 | */ 64 | - (AnyPromise *)promiseDataTaskWithRequest:(NSURLRequest *)request NS_REFINED_FOR_SWIFT; 65 | 66 | @end 67 | -------------------------------------------------------------------------------- /Pods/PromiseKit/Sources/wrap.swift: -------------------------------------------------------------------------------- 1 | /** 2 | Create a new pending promise by wrapping another asynchronous system. 3 | 4 | This initializer is convenient when wrapping asynchronous systems that 5 | use common patterns. For example: 6 | 7 | func fetchKitten() -> Promise { 8 | return PromiseKit.wrap { resolve in 9 | KittenFetcher.fetchWithCompletionBlock(resolve) 10 | } 11 | } 12 | 13 | - SeeAlso: Promise.init(resolvers:) 14 | */ 15 | public func wrap(_ body: (@escaping (T?, Error?) -> Void) throws -> Void) -> Promise { 16 | return Promise { fulfill, reject in 17 | try body { obj, err in 18 | if let err = err { 19 | reject(err) 20 | } else if let obj = obj { 21 | fulfill(obj) 22 | } else { 23 | reject(PMKError.invalidCallingConvention) 24 | } 25 | } 26 | } 27 | } 28 | 29 | /// For completion-handlers that eg. provide an enum or an error. 30 | public func wrap(_ body: (@escaping (T, Error?) -> Void) throws -> Void) -> Promise { 31 | return Promise { fulfill, reject in 32 | try body { obj, err in 33 | if let err = err { 34 | reject(err) 35 | } else { 36 | fulfill(obj) 37 | } 38 | } 39 | } 40 | } 41 | 42 | /// Some APIs unwisely invert the Cocoa standard for completion-handlers. 43 | public func wrap(_ body: (@escaping (Error?, T?) -> Void) throws -> Void) -> Promise { 44 | return Promise { fulfill, reject in 45 | try body { err, obj in 46 | if let err = err { 47 | reject(err) 48 | } else if let obj = obj { 49 | fulfill(obj) 50 | } else { 51 | reject(PMKError.invalidCallingConvention) 52 | } 53 | } 54 | } 55 | } 56 | 57 | /// For completion-handlers with just an optional Error 58 | public func wrap(_ body: (@escaping (Error?) -> Void) throws -> Void) -> Promise { 59 | return Promise { fulfill, reject in 60 | try body { error in 61 | if let error = error { 62 | reject(error) 63 | } else { 64 | fulfill() 65 | } 66 | } 67 | } 68 | } 69 | 70 | /// For completions that cannot error. 71 | public func wrap(_ body: (@escaping (T) -> Void) throws -> Void) -> Promise { 72 | return Promise { fulfill, _ in 73 | try body(fulfill) 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /viper/Presentation/UserStories/StartScreen/View/StartScreenViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StartScreenViewController.swift 3 | // viper 4 | // 5 | // Created by Sergii Avilov on 18/01/2017. 6 | // Copyright © 2017 Agilie. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class StartScreenViewController: UIViewController { 12 | 13 | // MARK: 14 | var output: StartScreenViewOutput! 15 | 16 | // MARK: Life cycle 17 | override func viewDidLoad() { 18 | super.viewDidLoad() 19 | output.viewIsReady() 20 | } 21 | 22 | override func viewWillAppear(_ animated: Bool) { 23 | super.viewWillAppear(animated) 24 | self.navigationController?.setNavigationBarHidden(true, animated: animated) 25 | } 26 | 27 | override func viewWillDisappear(_ animated: Bool) { 28 | super.viewWillDisappear(animated) 29 | self.navigationController?.setNavigationBarHidden(false, animated: animated) 30 | } 31 | 32 | // MARK: Outlets 33 | 34 | } 35 | 36 | // MARK: 37 | extension StartScreenViewController: ModuleInputProtocol { 38 | 39 | func setupInitialState(withArguments args: NamedValuesType, completion: ModuleCompletionHandler?) { 40 | output.setupInitialState(withArguments: args, completion: completion) 41 | } 42 | } 43 | 44 | // MARK: 45 | extension StartScreenViewController: StartScreenViewInput { 46 | 47 | } 48 | 49 | extension StartScreenViewController: UITableViewDataSource { 50 | 51 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 52 | return output.cities.count 53 | } 54 | 55 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 56 | guard let cell = tableView.dequeueReusableCell(withIdentifier: "cellIdentifier") else { return UITableViewCell() } 57 | let city = output.cities[indexPath.row] 58 | cell.textLabel?.text = city.title 59 | return cell 60 | } 61 | 62 | } 63 | 64 | extension StartScreenViewController: UITableViewDelegate { 65 | 66 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 67 | let city = output.cities[indexPath.row] 68 | showWeather(for: city.id) 69 | tableView.deselectRow(at: indexPath, animated: true) 70 | } 71 | 72 | } 73 | 74 | extension StartScreenViewController { 75 | 76 | func showWeather(for cityId: Int) { 77 | self.output.showWeatherDetails(cityId: cityId) 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /Pods/PromiseKit/Extensions/UIKit/Sources/UIView+AnyPromise.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+PromiseKit_UIAnimation.m 3 | // YahooDenaStudy 4 | // 5 | // Created by Masafumi Yoshida on 2014/07/11. 6 | // Copyright (c) 2014年 DeNA. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "UIView+AnyPromise.h" 11 | 12 | 13 | #define CopyPasta \ 14 | NSAssert([NSThread isMainThread], @"UIKit animation must be performed on the main thread"); \ 15 | \ 16 | if (![NSThread isMainThread]) { \ 17 | id error = [NSError errorWithDomain:PMKErrorDomain code:PMKInvalidUsageError userInfo:@{NSLocalizedDescriptionKey: @"Animation was attempted on a background thread"}]; \ 18 | return [AnyPromise promiseWithValue:error]; \ 19 | } \ 20 | \ 21 | PMKResolver resolve = nil; \ 22 | AnyPromise *promise = [[AnyPromise alloc] initWithResolver:&resolve]; 23 | 24 | 25 | @implementation UIView (PromiseKit) 26 | 27 | + (AnyPromise *)promiseWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations { 28 | return [self promiseWithDuration:duration delay:0 options:0 animations:animations]; 29 | } 30 | 31 | + (AnyPromise *)promiseWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewAnimationOptions)options animations:(void(^)(void))animations 32 | { 33 | CopyPasta; 34 | 35 | [UIView animateWithDuration:duration delay:delay options:options animations:animations completion:^(BOOL finished) { 36 | resolve(@(finished)); 37 | }]; 38 | 39 | return promise; 40 | } 41 | 42 | + (AnyPromise *)promiseWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay usingSpringWithDamping:(CGFloat)dampingRatio initialSpringVelocity:(CGFloat)velocity options:(UIViewAnimationOptions)options animations:(void(^)(void))animations 43 | { 44 | CopyPasta; 45 | 46 | [UIView animateWithDuration:duration delay:delay usingSpringWithDamping:dampingRatio initialSpringVelocity:velocity options:options animations:animations completion:^(BOOL finished) { 47 | resolve(@(finished)); 48 | }]; 49 | 50 | return promise; 51 | } 52 | 53 | + (AnyPromise *)promiseWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewKeyframeAnimationOptions)options keyframeAnimations:(void(^)(void))animations 54 | { 55 | CopyPasta; 56 | 57 | [UIView animateKeyframesWithDuration:duration delay:delay options:options animations:animations completion:^(BOOL finished) { 58 | resolve(@(finished)); 59 | }]; 60 | 61 | return promise; 62 | } 63 | 64 | @end 65 | -------------------------------------------------------------------------------- /Pods/ObjectMapper/Sources/URLTransform.swift: -------------------------------------------------------------------------------- 1 | // 2 | // URLTransform.swift 3 | // ObjectMapper 4 | // 5 | // Created by Tristan Himmelman on 2014-10-27. 6 | // 7 | // The MIT License (MIT) 8 | // 9 | // Copyright (c) 2014-2016 Hearst 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining a copy 12 | // of this software and associated documentation files (the "Software"), to deal 13 | // in the Software without restriction, including without limitation the rights 14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | // copies of the Software, and to permit persons to whom the Software is 16 | // furnished to do so, subject to the following conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be included in 19 | // all copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | // THE SOFTWARE. 28 | 29 | import Foundation 30 | 31 | open class URLTransform: TransformType { 32 | public typealias Object = URL 33 | public typealias JSON = String 34 | private let shouldEncodeURLString: Bool 35 | 36 | /** 37 | Initializes the URLTransform with an option to encode URL strings before converting them to an NSURL 38 | - parameter shouldEncodeUrlString: when true (the default) the string is encoded before passing 39 | to `NSURL(string:)` 40 | - returns: an initialized transformer 41 | */ 42 | public init(shouldEncodeURLString: Bool = true) { 43 | self.shouldEncodeURLString = shouldEncodeURLString 44 | } 45 | 46 | open func transformFromJSON(_ value: Any?) -> URL? { 47 | guard let URLString = value as? String else { return nil } 48 | 49 | if !shouldEncodeURLString { 50 | return URL(string: URLString) 51 | } 52 | 53 | guard let escapedURLString = URLString.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed) else { 54 | return nil 55 | } 56 | return URL(string: escapedURLString) 57 | } 58 | 59 | open func transformToJSON(_ value: URL?) -> String? { 60 | if let URL = value { 61 | return URL.absoluteString 62 | } 63 | return nil 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Pods/ObjectMapper/Sources/EnumOperators.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EnumOperators.swift 3 | // ObjectMapper 4 | // 5 | // Created by Tristan Himmelman on 2016-09-26. 6 | // Copyright © 2016 hearst. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | 12 | // MARK:- Raw Representable types 13 | 14 | /// Object of Raw Representable type 15 | public func <- (left: inout T, right: Map) { 16 | left <- (right, EnumTransform()) 17 | } 18 | 19 | public func >>> (left: T, right: Map) { 20 | left >>> (right, EnumTransform()) 21 | } 22 | 23 | 24 | /// Optional Object of Raw Representable type 25 | public func <- (left: inout T?, right: Map) { 26 | left <- (right, EnumTransform()) 27 | } 28 | 29 | public func >>> (left: T?, right: Map) { 30 | left >>> (right, EnumTransform()) 31 | } 32 | 33 | 34 | /// Implicitly Unwrapped Optional Object of Raw Representable type 35 | public func <- (left: inout T!, right: Map) { 36 | left <- (right, EnumTransform()) 37 | } 38 | 39 | // MARK:- Arrays of Raw Representable type 40 | 41 | /// Array of Raw Representable object 42 | public func <- (left: inout [T], right: Map) { 43 | left <- (right, EnumTransform()) 44 | } 45 | 46 | public func >>> (left: [T], right: Map) { 47 | left >>> (right, EnumTransform()) 48 | } 49 | 50 | 51 | /// Array of Raw Representable object 52 | public func <- (left: inout [T]?, right: Map) { 53 | left <- (right, EnumTransform()) 54 | } 55 | 56 | public func >>> (left: [T]?, right: Map) { 57 | left >>> (right, EnumTransform()) 58 | } 59 | 60 | 61 | /// Array of Raw Representable object 62 | public func <- (left: inout [T]!, right: Map) { 63 | left <- (right, EnumTransform()) 64 | } 65 | 66 | // MARK:- Dictionaries of Raw Representable type 67 | 68 | /// Dictionary of Raw Representable object 69 | public func <- (left: inout [String: T], right: Map) { 70 | left <- (right, EnumTransform()) 71 | } 72 | 73 | public func >>> (left: [String: T], right: Map) { 74 | left >>> (right, EnumTransform()) 75 | } 76 | 77 | 78 | /// Dictionary of Raw Representable object 79 | public func <- (left: inout [String: T]?, right: Map) { 80 | left <- (right, EnumTransform()) 81 | } 82 | 83 | public func >>> (left: [String: T]?, right: Map) { 84 | left >>> (right, EnumTransform()) 85 | } 86 | 87 | 88 | /// Dictionary of Raw Representable object 89 | public func <- (left: inout [String: T]!, right: Map) { 90 | left <- (right, EnumTransform()) 91 | } 92 | -------------------------------------------------------------------------------- /Pods/PromiseKit/Sources/GlobalState.m: -------------------------------------------------------------------------------- 1 | #import "PromiseKit.h" 2 | 3 | @interface NSError (PMK) 4 | - (BOOL)isCancelled; 5 | @end 6 | 7 | static dispatch_once_t __PMKDefaultDispatchQueueToken; 8 | static dispatch_queue_t __PMKDefaultDispatchQueue; 9 | 10 | dispatch_queue_t PMKDefaultDispatchQueue() { 11 | dispatch_once(&__PMKDefaultDispatchQueueToken, ^{ 12 | if (__PMKDefaultDispatchQueue == nil) { 13 | __PMKDefaultDispatchQueue = dispatch_get_main_queue(); 14 | } 15 | }); 16 | return __PMKDefaultDispatchQueue; 17 | } 18 | 19 | void PMKSetDefaultDispatchQueue(dispatch_queue_t newDefaultQueue) { 20 | dispatch_once(&__PMKDefaultDispatchQueueToken, ^{ 21 | __PMKDefaultDispatchQueue = newDefaultQueue; 22 | }); 23 | } 24 | 25 | 26 | static dispatch_once_t __PMKErrorUnhandlerToken; 27 | static void (^__PMKErrorUnhandler)(NSError *); 28 | 29 | void PMKUnhandledErrorHandler(NSError *error) { 30 | dispatch_once(&__PMKErrorUnhandlerToken, ^{ 31 | if (__PMKErrorUnhandler == nil) { 32 | __PMKErrorUnhandler = ^(NSError *error){ 33 | if (!error.isCancelled) { 34 | NSLog(@"PromiseKit: unhandled error: %@", error); 35 | } 36 | }; 37 | } 38 | }); 39 | return __PMKErrorUnhandler(error); 40 | } 41 | 42 | void PMKSetUnhandledErrorHandler(void(^newHandler)(NSError *)) { 43 | dispatch_once(&__PMKErrorUnhandlerToken, ^{ 44 | __PMKErrorUnhandler = newHandler; 45 | }); 46 | } 47 | 48 | 49 | static dispatch_once_t __PMKUnhandledExceptionHandlerToken; 50 | static NSError *(^__PMKUnhandledExceptionHandler)(id); 51 | 52 | NSError *PMKProcessUnhandledException(id thrown) { 53 | 54 | dispatch_once(&__PMKUnhandledExceptionHandlerToken, ^{ 55 | __PMKUnhandledExceptionHandler = ^id(id reason){ 56 | if ([reason isKindOfClass:[NSError class]]) 57 | return reason; 58 | if ([reason isKindOfClass:[NSString class]]) 59 | return [NSError errorWithDomain:PMKErrorDomain code:PMKUnexpectedError userInfo:@{NSLocalizedDescriptionKey: reason}]; 60 | return nil; 61 | }; 62 | }); 63 | 64 | id err = __PMKUnhandledExceptionHandler(thrown); 65 | if (!err) { 66 | NSLog(@"PromiseKit no longer catches *all* exceptions. However you can change this behavior by setting a new PMKProcessUnhandledException handler."); 67 | @throw thrown; 68 | } 69 | return err; 70 | } 71 | 72 | void PMKSetUnhandledExceptionHandler(NSError *(^newHandler)(id)) { 73 | dispatch_once(&__PMKUnhandledExceptionHandlerToken, ^{ 74 | __PMKUnhandledExceptionHandler = newHandler; 75 | }); 76 | } 77 | -------------------------------------------------------------------------------- /Pods/ObjectMapper/Sources/MapError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MapError.swift 3 | // ObjectMapper 4 | // 5 | // Created by Tristan Himmelman on 2016-09-26. 6 | // 7 | // The MIT License (MIT) 8 | // 9 | // Copyright (c) 2014-2016 Hearst 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining a copy 12 | // of this software and associated documentation files (the "Software"), to deal 13 | // in the Software without restriction, including without limitation the rights 14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | // copies of the Software, and to permit persons to whom the Software is 16 | // furnished to do so, subject to the following conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be included in 19 | // all copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | // THE SOFTWARE. 28 | 29 | import Foundation 30 | 31 | public struct MapError: Error { 32 | public var key: String? 33 | public var currentValue: Any? 34 | public var reason: String? 35 | public var file: StaticString? 36 | public var function: StaticString? 37 | public var line: UInt? 38 | 39 | public init(key: String?, currentValue: Any?, reason: String?, file: StaticString? = nil, function: StaticString? = nil, line: UInt? = nil) { 40 | self.key = key 41 | self.currentValue = currentValue 42 | self.reason = reason 43 | self.file = file 44 | self.function = function 45 | self.line = line 46 | } 47 | } 48 | 49 | extension MapError: CustomStringConvertible { 50 | 51 | private var location: String? { 52 | guard let file = file, let function = function, let line = line else { return nil } 53 | let fileName = ((String(describing: file).components(separatedBy: "/").last ?? "").components(separatedBy: ".").first ?? "") 54 | return "\(fileName).\(function):\(line)" 55 | } 56 | 57 | public var description: String { 58 | let info: [(String, Any?)] = [ 59 | ("- reason", reason), 60 | ("- location", location), 61 | ("- key", key), 62 | ("- currentValue", currentValue), 63 | ] 64 | let infoString = info.map { "\($0): \($1 ?? "nil")" }.joined(separator: "\n") 65 | return "Got an error while mapping.\n\(infoString)" 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/Notifications.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Notifications.swift 3 | // 4 | // Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | extension Notification.Name { 28 | /// Used as a namespace for all `URLSessionTask` related notifications. 29 | public struct Task { 30 | /// Posted when a `URLSessionTask` is resumed. The notification `object` contains the resumed `URLSessionTask`. 31 | public static let DidResume = Notification.Name(rawValue: "org.alamofire.notification.name.task.didResume") 32 | 33 | /// Posted when a `URLSessionTask` is suspended. The notification `object` contains the suspended `URLSessionTask`. 34 | public static let DidSuspend = Notification.Name(rawValue: "org.alamofire.notification.name.task.didSuspend") 35 | 36 | /// Posted when a `URLSessionTask` is cancelled. The notification `object` contains the cancelled `URLSessionTask`. 37 | public static let DidCancel = Notification.Name(rawValue: "org.alamofire.notification.name.task.didCancel") 38 | 39 | /// Posted when a `URLSessionTask` is completed. The notification `object` contains the completed `URLSessionTask`. 40 | public static let DidComplete = Notification.Name(rawValue: "org.alamofire.notification.name.task.didComplete") 41 | } 42 | } 43 | 44 | // MARK: - 45 | 46 | extension Notification { 47 | /// Used as a namespace for all `Notification` user info dictionary keys. 48 | public struct Key { 49 | /// User info dictionary key representing the `URLSessionTask` associated with the notification. 50 | public static let Task = "org.alamofire.notification.key.task" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /viper/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // viper 4 | // 5 | // Created by Sergii Avilov on 1/17/17. 6 | // Copyright © 2017 test. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | var appRoot: ApplicationRoot! 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 18 | 19 | // Setup services 20 | ServicesAssembly.setup(application: application) 21 | 22 | // Setup UI 23 | let navigationController = UINavigationController() 24 | 25 | window = UIWindow(frame: UIScreen.main.bounds) 26 | window!.rootViewController = navigationController 27 | window!.backgroundColor = UIColor.white 28 | window!.makeKeyAndVisible() 29 | 30 | // Start application 31 | appRoot = ApplicationRoot(withNavigation: navigationController) 32 | appRoot.start() 33 | 34 | return true 35 | } 36 | 37 | func applicationWillResignActive(_ application: UIApplication) { 38 | // 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. 39 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 40 | } 41 | 42 | func applicationDidEnterBackground(_ application: UIApplication) { 43 | // 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. 44 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 45 | } 46 | 47 | func applicationWillEnterForeground(_ application: UIApplication) { 48 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 49 | } 50 | 51 | func applicationDidBecomeActive(_ application: UIApplication) { 52 | // 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. 53 | } 54 | 55 | func applicationWillTerminate(_ application: UIApplication) { 56 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 57 | } 58 | 59 | 60 | } 61 | 62 | -------------------------------------------------------------------------------- /Pods/PromiseKit/Sources/NSMethodSignatureForBlock.m: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | struct PMKBlockLiteral { 4 | void *isa; // initialized to &_NSConcreteStackBlock or &_NSConcreteGlobalBlock 5 | int flags; 6 | int reserved; 7 | void (*invoke)(void *, ...); 8 | struct block_descriptor { 9 | unsigned long int reserved; // NULL 10 | unsigned long int size; // sizeof(struct Block_literal_1) 11 | // optional helper functions 12 | void (*copy_helper)(void *dst, void *src); // IFF (1<<25) 13 | void (*dispose_helper)(void *src); // IFF (1<<25) 14 | // required ABI.2010.3.16 15 | const char *signature; // IFF (1<<30) 16 | } *descriptor; 17 | // imported variables 18 | }; 19 | 20 | typedef NS_OPTIONS(NSUInteger, PMKBlockDescriptionFlags) { 21 | PMKBlockDescriptionFlagsHasCopyDispose = (1 << 25), 22 | PMKBlockDescriptionFlagsHasCtor = (1 << 26), // helpers have C++ code 23 | PMKBlockDescriptionFlagsIsGlobal = (1 << 28), 24 | PMKBlockDescriptionFlagsHasStret = (1 << 29), // IFF BLOCK_HAS_SIGNATURE 25 | PMKBlockDescriptionFlagsHasSignature = (1 << 30) 26 | }; 27 | 28 | // It appears 10.7 doesn't support quotes in method signatures. Remove them 29 | // via @rabovik's method. See https://github.com/OliverLetterer/SLObjectiveCRuntimeAdditions/pull/2 30 | #if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_8 31 | NS_INLINE static const char * pmk_removeQuotesFromMethodSignature(const char *str){ 32 | char *result = malloc(strlen(str) + 1); 33 | BOOL skip = NO; 34 | char *to = result; 35 | char c; 36 | while ((c = *str++)) { 37 | if ('"' == c) { 38 | skip = !skip; 39 | continue; 40 | } 41 | if (skip) continue; 42 | *to++ = c; 43 | } 44 | *to = '\0'; 45 | return result; 46 | } 47 | #endif 48 | 49 | static NSMethodSignature *NSMethodSignatureForBlock(id block) { 50 | if (!block) 51 | return nil; 52 | 53 | struct PMKBlockLiteral *blockRef = (__bridge struct PMKBlockLiteral *)block; 54 | PMKBlockDescriptionFlags flags = (PMKBlockDescriptionFlags)blockRef->flags; 55 | 56 | if (flags & PMKBlockDescriptionFlagsHasSignature) { 57 | void *signatureLocation = blockRef->descriptor; 58 | signatureLocation += sizeof(unsigned long int); 59 | signatureLocation += sizeof(unsigned long int); 60 | 61 | if (flags & PMKBlockDescriptionFlagsHasCopyDispose) { 62 | signatureLocation += sizeof(void(*)(void *dst, void *src)); 63 | signatureLocation += sizeof(void (*)(void *src)); 64 | } 65 | 66 | const char *signature = (*(const char **)signatureLocation); 67 | #if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_8 68 | signature = pmk_removeQuotesFromMethodSignature(signature); 69 | NSMethodSignature *nsSignature = [NSMethodSignature signatureWithObjCTypes:signature]; 70 | free((void *)signature); 71 | 72 | return nsSignature; 73 | #endif 74 | return [NSMethodSignature signatureWithObjCTypes:signature]; 75 | } 76 | return 0; 77 | } 78 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ######################### 2 | # .gitignore file for Xcode4 / OS X Source projects 3 | # 4 | # NB: if you are storing "built" products, this WILL NOT WORK, 5 | # and you should use a different .gitignore (or none at all) 6 | # This file is for SOURCE projects, where there are many extra 7 | # files that we want to exclude 8 | # 9 | # For updates, see: http://stackoverflow.com/questions/49478/git-ignore-file-for-xcode-projects 10 | ######################### 11 | 12 | ##### 13 | # OS X temporary files that should never be committed 14 | 15 | .DS_Store 16 | *.swp 17 | profile 18 | 19 | 20 | #### 21 | # Xcode temporary files that should never be committed 22 | # 23 | # NB: NIB/XIB files still exist even on Storyboard projects, so we want this... 24 | 25 | *~.nib 26 | 27 | 28 | #### 29 | # Xcode build files - 30 | # 31 | # NB: slash on the end, so we only remove the FOLDER, not any files that were badly named "DerivedData" 32 | 33 | DerivedData/ 34 | 35 | # NB: slash on the end, so we only remove the FOLDER, not any files that were badly named "build" 36 | 37 | build/ 38 | 39 | 40 | ##### 41 | # Xcode private settings (window sizes, bookmarks, breakpoints, custom executables, smart groups) 42 | # 43 | # This is complicated: 44 | # 45 | # SOMETIMES you need to put this file in version control. 46 | # Apple designed it poorly - if you use "custom executables", they are 47 | # saved in this file. 48 | # 99% of projects do NOT use those, so they do NOT want to version control this file. 49 | # ..but if you're in the 1%, comment out the line "*.pbxuser" 50 | 51 | *.pbxuser 52 | *.mode1v3 53 | *.mode2v3 54 | *.perspectivev3 55 | # NB: also, whitelist the default ones, some projects need to use these 56 | !default.pbxuser 57 | !default.mode1v3 58 | !default.mode2v3 59 | !default.perspectivev3 60 | 61 | 62 | #### 63 | # Xcode 4 - semi-personal settings, often included in workspaces 64 | # 65 | # You can safely ignore the xcuserdata files - but do NOT ignore the files next to them 66 | # 67 | 68 | xcuserdata 69 | 70 | .idea 71 | 72 | #### 73 | # XCode 4 workspaces - more detailed 74 | # 75 | # Workspaces are important! They are a core feature of Xcode - don't exclude them :) 76 | # 77 | # Workspace layout is quite spammy. For reference: 78 | # 79 | # (root)/ 80 | # (project-name).xcodeproj/ 81 | # project.pbxproj 82 | # project.xcworkspace/ 83 | # contents.xcworkspacedata 84 | # xcuserdata/ 85 | # (your name)/xcuserdatad/ 86 | # xcuserdata/ 87 | # (your name)/xcuserdatad/ 88 | # 89 | # 90 | # 91 | # Xcode 4 workspaces - SHARED 92 | # 93 | # This is UNDOCUMENTED (google: "developer.apple.com xcshareddata" - 0 results 94 | # But if you're going to kill personal workspaces, at least keep the shared ones... 95 | # 96 | # 97 | !xcshareddata 98 | 99 | 100 | #### 101 | # Xcode 4 - Deprecated classes 102 | # 103 | # Allegedly, if you manually "deprecate" your classes, they get moved here. 104 | # 105 | # We're using source-control, so this is a "feature" that we do not want! 106 | 107 | *.moved-aside 108 | 109 | 110 | #### 111 | # UNKNOWN: recommended by others, but I can't discover what these files are 112 | # 113 | # ...none. Everything is now explained. 114 | -------------------------------------------------------------------------------- /Pods/PromiseKit/Extensions/UIKit/Sources/UIView+AnyPromise.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | // Created by Masafumi Yoshida on 2014/07/11. 5 | // Copyright (c) 2014年 DeNA. All rights reserved. 6 | 7 | /** 8 | To import the `UIView` category: 9 | 10 | use_frameworks! 11 | pod "PromiseKit/UIKit" 12 | 13 | Or `UIKit` is one of the categories imported by the umbrella pod: 14 | 15 | use_frameworks! 16 | pod "PromiseKit" 17 | 18 | And then in your sources: 19 | 20 | @import PromiseKit; 21 | */ 22 | @interface UIView (PromiseKit) 23 | 24 | /** 25 | Animate changes to one or more views using the specified duration. 26 | 27 | @param duration The total duration of the animations, measured in 28 | seconds. If you specify a negative value or 0, the changes are made 29 | without animating them. 30 | 31 | @param animations A block object containing the changes to commit to the 32 | views. 33 | 34 | @return A promise that fulfills with a boolean NSNumber indicating 35 | whether or not the animations actually finished. 36 | */ 37 | + (AnyPromise *)promiseWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations NS_REFINED_FOR_SWIFT; 38 | 39 | /** 40 | Animate changes to one or more views using the specified duration, delay, 41 | options, and completion handler. 42 | 43 | @param duration The total duration of the animations, measured in 44 | seconds. If you specify a negative value or 0, the changes are made 45 | without animating them. 46 | 47 | @param delay The amount of time (measured in seconds) to wait before 48 | beginning the animations. Specify a value of 0 to begin the animations 49 | immediately. 50 | 51 | @param options A mask of options indicating how you want to perform the 52 | animations. For a list of valid constants, see UIViewAnimationOptions. 53 | 54 | @param animations A block object containing the changes to commit to the 55 | views. 56 | 57 | @return A promise that fulfills with a boolean NSNumber indicating 58 | whether or not the animations actually finished. 59 | */ 60 | + (AnyPromise *)promiseWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewAnimationOptions)options animations:(void (^)(void))animations NS_REFINED_FOR_SWIFT; 61 | 62 | /** 63 | Performs a view animation using a timing curve corresponding to the 64 | motion of a physical spring. 65 | 66 | @return A promise that fulfills with a boolean NSNumber indicating 67 | whether or not the animations actually finished. 68 | */ 69 | + (AnyPromise *)promiseWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay usingSpringWithDamping:(CGFloat)dampingRatio initialSpringVelocity:(CGFloat)velocity options:(UIViewAnimationOptions)options animations:(void (^)(void))animations NS_REFINED_FOR_SWIFT; 70 | 71 | /** 72 | Creates an animation block object that can be used to set up 73 | keyframe-based animations for the current view. 74 | 75 | @return A promise that fulfills with a boolean NSNumber indicating 76 | whether or not the animations actually finished. 77 | */ 78 | + (AnyPromise *)promiseWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewKeyframeAnimationOptions)options keyframeAnimations:(void (^)(void))animations NS_REFINED_FOR_SWIFT; 79 | 80 | @end 81 | -------------------------------------------------------------------------------- /Pods/PromiseKit/Sources/Zalgo.swift: -------------------------------------------------------------------------------- 1 | import class Dispatch.DispatchQueue 2 | import class Foundation.Thread 3 | 4 | /** 5 | `zalgo` causes your handlers to be executed as soon as their promise resolves. 6 | 7 | Usually all handlers are dispatched to a queue (the main queue by default); the `on:` parameter of `then` configures this. Its default value is `DispatchQueue.main`. 8 | 9 | - Important: `zalgo` is dangerous. 10 | 11 | Compare: 12 | 13 | var x = 0 14 | foo.then { 15 | print(x) // => 1 16 | } 17 | x++ 18 | 19 | With: 20 | 21 | var x = 0 22 | foo.then(on: zalgo) { 23 | print(x) // => 0 or 1 24 | } 25 | x++ 26 | 27 | In the latter case the value of `x` may be `0` or `1` depending on whether `foo` is resolved. This is a race-condition that is easily avoided by not using `zalgo`. 28 | 29 | - Important: you cannot control the queue that your handler executes if using `zalgo`. 30 | 31 | - Note: `zalgo` is provided for libraries providing promises that have good tests that prove “Unleashing Zalgo” is safe. You can also use it in your application code in situations where performance is critical, but be careful: read the essay liked below to understand the risks. 32 | 33 | - SeeAlso: [Designing APIs for Asynchrony](http://blog.izs.me/post/59142742143/designing-apis-for-asynchrony) 34 | - SeeAlso: `waldo` 35 | */ 36 | public let zalgo = DispatchQueue(label: "Zalgo") 37 | 38 | /** 39 | `waldo` is dangerous. 40 | 41 | `waldo` is `zalgo`, unless the current queue is the main thread, in which 42 | case we dispatch to the default background queue. 43 | 44 | If your block is likely to take more than a few milliseconds to execute, 45 | then you should use waldo: 60fps means the main thread cannot hang longer 46 | than 17 milliseconds: don’t contribute to UI lag. 47 | 48 | Conversely if your then block is trivial, use zalgo: GCD is not free and 49 | for whatever reason you may already be on the main thread so just do what 50 | you are doing quickly and pass on execution. 51 | 52 | It is considered good practice for asynchronous APIs to complete onto the 53 | main thread. Apple do not always honor this, nor do other developers. 54 | However, they *should*. In that respect waldo is a good choice if your 55 | then is going to take some time and doesn’t interact with the UI. 56 | 57 | Please note (again) that generally you should not use `zalgo` or `waldo`. 58 | The performance gains are negligible and we provide these functions only out 59 | of a misguided sense that library code should be as optimized as possible. 60 | If you use either without tests proving their correctness you may 61 | unwillingly introduce horrendous, near-impossible-to-trace bugs. 62 | 63 | - SeeAlso: `zalgo` 64 | */ 65 | public let waldo = DispatchQueue(label: "Waldo") 66 | 67 | 68 | @inline(__always) func contain_zalgo(_ q: DispatchQueue, body: @escaping () -> Void) { 69 | if q === zalgo || q === waldo && !Thread.isMainThread { 70 | body() 71 | } else { 72 | q.async(execute: body) 73 | } 74 | } 75 | 76 | @inline(__always) func contain_zalgo(_ q: DispatchQueue, rejecter reject: @escaping (Resolution) -> Void, block: @escaping () throws -> Void) { 77 | contain_zalgo(q) { 78 | do { try block() } catch { reject(Resolution(error)) } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /Pods/Compass/Sources/Compass.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public struct Compass { 4 | 5 | typealias Result = ( 6 | route: String, 7 | arguments: [String: String], 8 | concreteMatchCount: Int, 9 | wildcardMatchCount: Int) 10 | 11 | fileprivate static var internalScheme = "" 12 | public static var delimiter: String = ":" 13 | 14 | public static var scheme: String { 15 | set { Compass.internalScheme = newValue } 16 | get { return "\(Compass.internalScheme)://" } 17 | } 18 | 19 | public static var routes = [String]() 20 | 21 | public static func parse(url: URL, payload: Any? = nil) -> Location? { 22 | let path = url.absoluteString.substring(from: scheme.endIndex) 23 | 24 | guard !(path.contains("?") || path.contains("#")) else { 25 | return parseComponents(url: url, payload: payload) 26 | } 27 | 28 | let results: [Result] = routes.flatMap { 29 | return findMatch(routeString: $0, pathString: path) 30 | }.sorted { (r1: Result, r2: Result) in 31 | if r1.concreteMatchCount == r2.concreteMatchCount { 32 | return r1.wildcardMatchCount > r2.wildcardMatchCount 33 | } 34 | 35 | return r1.concreteMatchCount > r2.concreteMatchCount 36 | } 37 | 38 | if let result = results.first { 39 | return Location(path: result.route, arguments: result.arguments, payload: payload) 40 | } 41 | 42 | return nil 43 | } 44 | 45 | static func parseComponents(url: URL, payload: Any? = nil) -> Location? { 46 | guard let route = url.host else { return nil } 47 | 48 | let urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false) 49 | var arguments = [String : String]() 50 | 51 | urlComponents?.queryItems?.forEach { queryItem in 52 | arguments[queryItem.name] = queryItem.value 53 | } 54 | 55 | if let fragment = urlComponents?.fragment { 56 | arguments = fragment.queryParameters() 57 | } 58 | 59 | return Location(path: route, arguments: arguments, payload: payload) 60 | } 61 | 62 | static func findMatch(routeString: String, pathString: String) -> Result? { 63 | let routes = routeString.split(delimiter) 64 | let paths = pathString.split(delimiter) 65 | 66 | guard routes.count == paths.count else { return nil } 67 | 68 | var arguments: [String: String] = [:] 69 | var concreteMatchCount = 0 70 | var wildcardMatchCount = 0 71 | 72 | for (route, path) in zip(routes, paths) { 73 | if route.hasPrefix("{") { 74 | let key = route.replacingOccurrences(of: "{", with: "") 75 | .replacingOccurrences(of: "}", with: "") 76 | arguments[key] = path 77 | 78 | wildcardMatchCount += 1 79 | continue 80 | } 81 | 82 | if route == path { 83 | concreteMatchCount += 1 84 | } else { 85 | return nil 86 | } 87 | } 88 | 89 | return (route: routeString, 90 | arguments: arguments, 91 | concreteMatchCount: concreteMatchCount, 92 | wildcardMatchCount: wildcardMatchCount) 93 | } 94 | } 95 | 96 | extension Compass { 97 | 98 | public static func navigate(to urn: String, scheme: String = Compass.scheme) { 99 | guard let url = URL(string: "\(scheme)\(urn)") else { return } 100 | open(url: url) 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /Pods/PromiseKit/Sources/AnyPromise.m: -------------------------------------------------------------------------------- 1 | #import "PMKCallVariadicBlock.m" 2 | #import "AnyPromise+Private.h" 3 | 4 | extern dispatch_queue_t PMKDefaultDispatchQueue(); 5 | 6 | NSString *const PMKErrorDomain = @"PMKErrorDomain"; 7 | 8 | 9 | @implementation AnyPromise (objc) 10 | 11 | - (instancetype)initWithResolver:(PMKResolver __strong *)resolver { 12 | return [[self class] promiseWithResolverBlock:^(PMKResolver resolve){ 13 | *resolver = resolve; 14 | }]; 15 | } 16 | 17 | - (AnyPromise *(^)(id))then { 18 | return ^(id block) { 19 | return [self __thenOn:PMKDefaultDispatchQueue() execute:^(id obj) { 20 | return PMKCallVariadicBlock(block, obj); 21 | }]; 22 | }; 23 | } 24 | 25 | - (AnyPromise *(^)(dispatch_queue_t, id))thenOn { 26 | return ^(dispatch_queue_t queue, id block) { 27 | return [self __thenOn:queue execute:^(id obj) { 28 | return PMKCallVariadicBlock(block, obj); 29 | }]; 30 | }; 31 | } 32 | 33 | - (AnyPromise *(^)(id))thenInBackground { 34 | return ^(id block) { 35 | return [self __thenOn:dispatch_get_global_queue(0, 0) execute:^(id obj) { 36 | return PMKCallVariadicBlock(block, obj); 37 | }]; 38 | }; 39 | } 40 | 41 | - (AnyPromise *(^)(id))catch { 42 | return ^(id block) { 43 | return [self __catchWithPolicy:PMKCatchPolicyAllErrorsExceptCancellation execute:^(id obj) { 44 | return PMKCallVariadicBlock(block, obj); 45 | }]; 46 | }; 47 | } 48 | 49 | - (AnyPromise *(^)(PMKCatchPolicy, id))catchWithPolicy { 50 | return ^(PMKCatchPolicy policy, id block) { 51 | return [self __catchWithPolicy:policy execute:^(id obj) { 52 | return PMKCallVariadicBlock(block, obj); 53 | }]; 54 | }; 55 | } 56 | 57 | - (AnyPromise *(^)(dispatch_block_t))always { 58 | return ^(dispatch_block_t block) { 59 | return [self __alwaysOn:PMKDefaultDispatchQueue() execute:block]; 60 | }; 61 | } 62 | 63 | - (AnyPromise *(^)(dispatch_queue_t, dispatch_block_t))alwaysOn { 64 | return ^(dispatch_queue_t queue, dispatch_block_t block) { 65 | return [self __alwaysOn:queue execute:block]; 66 | }; 67 | } 68 | 69 | @end 70 | 71 | 72 | 73 | @implementation AnyPromise (Adapters) 74 | 75 | + (instancetype)promiseWithAdapterBlock:(void (^)(PMKAdapter))block { 76 | return [self promiseWithResolverBlock:^(PMKResolver resolve) { 77 | block(^(id value, id error){ 78 | resolve(error ?: value); 79 | }); 80 | }]; 81 | } 82 | 83 | + (instancetype)promiseWithIntegerAdapterBlock:(void (^)(PMKIntegerAdapter))block { 84 | return [self promiseWithResolverBlock:^(PMKResolver resolve) { 85 | block(^(NSInteger value, id error){ 86 | if (error) { 87 | resolve(error); 88 | } else { 89 | resolve(@(value)); 90 | } 91 | }); 92 | }]; 93 | } 94 | 95 | + (instancetype)promiseWithBooleanAdapterBlock:(void (^)(PMKBooleanAdapter adapter))block { 96 | return [self promiseWithResolverBlock:^(PMKResolver resolve) { 97 | block(^(BOOL value, id error){ 98 | if (error) { 99 | resolve(error); 100 | } else { 101 | resolve(@(value)); 102 | } 103 | }); 104 | }]; 105 | } 106 | 107 | - (id)value { 108 | id obj = [self valueForKey:@"__value"]; 109 | 110 | if ([obj isKindOfClass:[PMKArray class]]) { 111 | return obj[0]; 112 | } else { 113 | return obj; 114 | } 115 | } 116 | 117 | @end 118 | -------------------------------------------------------------------------------- /viper.xcodeproj/xcuserdata/avilov.xcuserdatad/xcschemes/viper.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /Pods/ObjectMapper/Sources/HexColorTransform.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HexColorTransform.swift 3 | // ObjectMapper 4 | // 5 | // Created by Vitaliy Kuzmenko on 10/10/16. 6 | // Copyright © 2016 hearst. All rights reserved. 7 | // 8 | 9 | #if os(iOS) || os(tvOS) || os(watchOS) 10 | import UIKit 11 | #elseif os(macOS) 12 | import Cocoa 13 | #endif 14 | 15 | #if os(iOS) || os(tvOS) || os(watchOS) || os(macOS) 16 | open class HexColorTransform: TransformType { 17 | 18 | #if os(iOS) || os(tvOS) || os(watchOS) 19 | public typealias Object = UIColor 20 | #else 21 | public typealias Object = NSColor 22 | #endif 23 | 24 | public typealias JSON = String 25 | 26 | var prefix: Bool = false 27 | 28 | var alpha: Bool = false 29 | 30 | public init(prefixToJSON: Bool = false, alphaToJSON: Bool = false) { 31 | alpha = alphaToJSON 32 | prefix = prefixToJSON 33 | } 34 | 35 | open func transformFromJSON(_ value: Any?) -> Object? { 36 | if let rgba = value as? String { 37 | if rgba.hasPrefix("#") { 38 | let index = rgba.characters.index(rgba.startIndex, offsetBy: 1) 39 | let hex = rgba.substring(from: index) 40 | return getColor(hex: hex) 41 | } else { 42 | return getColor(hex: rgba) 43 | } 44 | } 45 | return nil 46 | } 47 | 48 | open func transformToJSON(_ value: Object?) -> JSON? { 49 | if let value = value { 50 | return hexString(color: value) 51 | } 52 | return nil 53 | } 54 | 55 | fileprivate func hexString(color: Object) -> String { 56 | let comps = color.cgColor.components! 57 | let r = Int(comps[0] * 255) 58 | let g = Int(comps[1] * 255) 59 | let b = Int(comps[2] * 255) 60 | let a = Int(comps[3] * 255) 61 | var hexString: String = "" 62 | if prefix { 63 | hexString = "#" 64 | } 65 | hexString += String(format: "%02X%02X%02X", r, g, b) 66 | 67 | if alpha { 68 | hexString += String(format: "%02X", a) 69 | } 70 | return hexString 71 | } 72 | 73 | fileprivate func getColor(hex: String) -> Object? { 74 | var red: CGFloat = 0.0 75 | var green: CGFloat = 0.0 76 | var blue: CGFloat = 0.0 77 | var alpha: CGFloat = 1.0 78 | 79 | let scanner = Scanner(string: hex) 80 | var hexValue: CUnsignedLongLong = 0 81 | if scanner.scanHexInt64(&hexValue) { 82 | switch (hex.characters.count) { 83 | case 3: 84 | red = CGFloat((hexValue & 0xF00) >> 8) / 15.0 85 | green = CGFloat((hexValue & 0x0F0) >> 4) / 15.0 86 | blue = CGFloat(hexValue & 0x00F) / 15.0 87 | case 4: 88 | red = CGFloat((hexValue & 0xF000) >> 12) / 15.0 89 | green = CGFloat((hexValue & 0x0F00) >> 8) / 15.0 90 | blue = CGFloat((hexValue & 0x00F0) >> 4) / 15.0 91 | alpha = CGFloat(hexValue & 0x000F) / 15.0 92 | case 6: 93 | red = CGFloat((hexValue & 0xFF0000) >> 16) / 255.0 94 | green = CGFloat((hexValue & 0x00FF00) >> 8) / 255.0 95 | blue = CGFloat(hexValue & 0x0000FF) / 255.0 96 | case 8: 97 | red = CGFloat((hexValue & 0xFF000000) >> 24) / 255.0 98 | green = CGFloat((hexValue & 0x00FF0000) >> 16) / 255.0 99 | blue = CGFloat((hexValue & 0x0000FF00) >> 8) / 255.0 100 | alpha = CGFloat(hexValue & 0x000000FF) / 255.0 101 | default: 102 | // Invalid RGB string, number of characters after '#' should be either 3, 4, 6 or 8 103 | return nil 104 | } 105 | } else { 106 | // "Scan hex error 107 | return nil 108 | } 109 | #if os(iOS) || os(tvOS) || os(watchOS) 110 | return UIColor(red: red, green: green, blue: blue, alpha: alpha) 111 | #else 112 | return NSColor(calibratedRed: red, green: green, blue: blue, alpha: alpha) 113 | #endif 114 | } 115 | } 116 | #endif 117 | -------------------------------------------------------------------------------- /Pods/ObjectMapper/Sources/IntegerOperators.swift: -------------------------------------------------------------------------------- 1 | // 2 | // IntegerOperators.swift 3 | // ObjectMapper 4 | // 5 | // Created by Suyeol Jeon on 17/02/2017. 6 | // Copyright © 2017 hearst. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | // MARK: - Signed Integer 12 | 13 | /// SignedInteger mapping 14 | public func <- (left: inout T, right: Map) { 15 | switch right.mappingType { 16 | case .fromJSON where right.isKeyPresent: 17 | let value: T = toSignedInteger(right.currentValue) ?? 0 18 | FromJSON.basicType(&left, object: value) 19 | case .toJSON: 20 | left >>> right 21 | default: () 22 | } 23 | } 24 | 25 | /// Optional SignedInteger mapping 26 | public func <- (left: inout T?, right: Map) { 27 | switch right.mappingType { 28 | case .fromJSON where right.isKeyPresent: 29 | let value: T? = toSignedInteger(right.currentValue) 30 | FromJSON.basicType(&left, object: value) 31 | case .toJSON: 32 | left >>> right 33 | default: () 34 | } 35 | } 36 | 37 | /// ImplicitlyUnwrappedOptional SignedInteger mapping 38 | public func <- (left: inout T!, right: Map) { 39 | switch right.mappingType { 40 | case .fromJSON where right.isKeyPresent: 41 | let value: T! = toSignedInteger(right.currentValue) 42 | FromJSON.basicType(&left, object: value) 43 | case .toJSON: 44 | left >>> right 45 | default: () 46 | } 47 | } 48 | 49 | 50 | // MARK: - Unsigned Integer 51 | 52 | /// UnsignedInteger mapping 53 | public func <- (left: inout T, right: Map) { 54 | switch right.mappingType { 55 | case .fromJSON where right.isKeyPresent: 56 | let value: T = toUnsignedInteger(right.currentValue) ?? 0 57 | FromJSON.basicType(&left, object: value) 58 | case .toJSON: 59 | left >>> right 60 | default: () 61 | } 62 | } 63 | 64 | 65 | /// Optional UnsignedInteger mapping 66 | public func <- (left: inout T?, right: Map) { 67 | switch right.mappingType { 68 | case .fromJSON where right.isKeyPresent: 69 | let value: T? = toUnsignedInteger(right.currentValue) 70 | FromJSON.basicType(&left, object: value) 71 | case .toJSON: 72 | left >>> right 73 | default: () 74 | } 75 | } 76 | 77 | /// ImplicitlyUnwrappedOptional UnsignedInteger mapping 78 | public func <- (left: inout T!, right: Map) { 79 | switch right.mappingType { 80 | case .fromJSON where right.isKeyPresent: 81 | let value: T! = toUnsignedInteger(right.currentValue) 82 | FromJSON.basicType(&left, object: value) 83 | case .toJSON: 84 | left >>> right 85 | default: () 86 | } 87 | } 88 | 89 | // MARK: - Casting Utils 90 | 91 | /// Convert any value to `SignedInteger`. 92 | private func toSignedInteger(_ value: Any?) -> T? { 93 | guard let value = value else { return nil } 94 | let max: IntMax 95 | switch value { 96 | case let x as Int: max = .init(x) 97 | case let x as Int8: max = .init(x) 98 | case let x as Int16: max = .init(x) 99 | case let x as Int32: max = .init(x) 100 | case let x as Int64: max = .init(x) 101 | case let x as UInt: max = .init(x) 102 | case let x as UInt8: max = .init(x) 103 | case let x as UInt16: max = .init(x) 104 | case let x as UInt32: max = .init(x) 105 | case let x as UInt64: max = .init(x) 106 | default: return nil 107 | } 108 | return T.init(max) 109 | } 110 | 111 | /// Convert any value to `UnsignedInteger`. 112 | private func toUnsignedInteger(_ value: Any?) -> T? { 113 | guard let value = value else { return nil } 114 | let max: UIntMax 115 | switch value { 116 | case let x as Int: max = .init(x) 117 | case let x as Int8: max = .init(x) 118 | case let x as Int16: max = .init(x) 119 | case let x as Int32: max = .init(x) 120 | case let x as Int64: max = .init(x) 121 | case let x as UInt: max = .init(x) 122 | case let x as UInt8: max = .init(x) 123 | case let x as UInt16: max = .init(x) 124 | case let x as UInt32: max = .init(x) 125 | case let x as UInt64: max = .init(x) 126 | default: return nil 127 | } 128 | return T.init(max) 129 | } 130 | -------------------------------------------------------------------------------- /viper/Presentation/Routing/AppRouter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppRouterProtocolImpl.swift 3 | // viper 4 | // 5 | // Created by Sergii Avilov on 1/17/17. 6 | // Copyright © 2017 test. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Compass 11 | 12 | class AppRouterImpl: AppRouterProtocol { 13 | 14 | // MARK: 15 | private let modulesList: Array 16 | private let _navigationController: UINavigationController 17 | 18 | init(withNavigation controller: UINavigationController, modules: Array, urlScheme: String) { 19 | 20 | _navigationController = controller 21 | _navigationController.setNavigationBarHidden(true, animated: false) 22 | modulesList = modules 23 | 24 | Compass.scheme = urlScheme 25 | Compass.routes = modulesList.map { 26 | return $0.moduleURN 27 | } 28 | 29 | } 30 | 31 | // MARK: 32 | 33 | var navigationController: UINavigationController { 34 | return _navigationController 35 | } 36 | 37 | func pushModule(byUrn urn: String, animated: Bool, completion: ModuleCompletionHandler?) { 38 | 39 | guard let url = URL(string:"\(Compass.scheme)\(urn)") else { 40 | fatalError("Invalid URN: \(urn)") 41 | } 42 | 43 | guard let controller = createModule(byUrl: url, completion: completion) else { 44 | fatalError("Can't create controller by URL: \(url)") 45 | } 46 | 47 | navigationController.pushViewController(controller, animated: animated) 48 | } 49 | 50 | func replaceViewStack(rootUrn urn: String, animated: Bool, completion: ModuleCompletionHandler?) { 51 | 52 | guard let url = URL(string:"\(Compass.scheme)\(urn)") else { 53 | fatalError("Invalid URN: \(urn)") 54 | } 55 | 56 | guard let controller = createModule(byUrl: url, completion: completion) else { 57 | fatalError("Can't create controller by URL: \(url)") 58 | } 59 | 60 | navigationController.setViewControllers([controller], animated: animated) 61 | } 62 | 63 | func presentModule(byUrn urn: String, animated: Bool, completion: ModuleCompletionHandler?) { 64 | 65 | guard let url = URL(string:"\(Compass.scheme)\(urn)") else { 66 | fatalError("Invalid URN: \(urn)") 67 | } 68 | 69 | guard let controller = createModule(byUrl: url, completion: completion) else { 70 | fatalError("Can't create controller by URL: \(url)") 71 | } 72 | 73 | navigationController.present(controller, animated: animated, completion: nil) 74 | } 75 | 76 | // 77 | 78 | func popToRootController(animated: Bool) { 79 | navigationController.popToRootViewController(animated: animated) 80 | } 81 | 82 | func popCurrentController(animated: Bool) { 83 | navigationController.popViewController(animated: animated) 84 | } 85 | 86 | func popToViewController(_ controller: UIViewController, animated: Bool) { 87 | navigationController.popToViewController(controller, animated: animated) 88 | } 89 | 90 | func dismissCurrentController(animated: Bool) { 91 | navigationController.dismiss(animated: animated, completion: nil) 92 | } 93 | 94 | // MARK: 95 | 96 | private func createModule(byUrl url: URL, completion: ModuleCompletionHandler?) -> UIViewController? { 97 | 98 | guard let location = Compass.parse(url: url) else { 99 | return nil 100 | } 101 | 102 | let arguments = location.arguments 103 | 104 | NSLog("Create module with path: '\(location.path)', arguments: '\(arguments)'") 105 | 106 | let factory = modulesList.first { 107 | return location.path == $0.moduleURN 108 | } 109 | 110 | return factory?.createModule(arguments: arguments, completion: completion) 111 | } 112 | 113 | } 114 | -------------------------------------------------------------------------------- /Pods/PromiseKit/Extensions/UIKit/Sources/PMKAlertController.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | #if !COCOAPODS 3 | import PromiseKit 4 | #endif 5 | 6 | //TODO tests 7 | //TODO NSCoding 8 | 9 | /** 10 | A “promisable” UIAlertController. 11 | 12 | UIAlertController is not a suitable API for an extension; it has closure 13 | handlers on its main API for each button and an extension would have to 14 | either replace all these when the controller is presented or force you to 15 | use an extended addAction method, which would be easy to forget part of 16 | the time. Hence we provide a facade pattern that can be promised. 17 | 18 | let alert = PMKAlertController("OHAI") 19 | let sup = alert.addActionWithTitle("SUP") 20 | let bye = alert.addActionWithTitle("BYE") 21 | promiseViewController(alert).then { action in 22 | switch action { 23 | case is sup: 24 | //… 25 | case is bye: 26 | //… 27 | } 28 | } 29 | */ 30 | public class PMKAlertController { 31 | /// The title of the alert. 32 | public var title: String? { return UIAlertController.title } 33 | /// Descriptive text that provides more details about the reason for the alert. 34 | public var message: String? { return UIAlertController.message } 35 | /// The style of the alert controller. 36 | public var preferredStyle: UIAlertControllerStyle { return UIAlertController.preferredStyle } 37 | /// The actions that the user can take in response to the alert or action sheet. 38 | public var actions: [UIAlertAction] { return UIAlertController.actions } 39 | /// The array of text fields displayed by the alert. 40 | public var textFields: [UITextField]? { return UIAlertController.textFields } 41 | 42 | #if !os(tvOS) 43 | /// The nearest popover presentation controller that is managing the current view controller. 44 | public var popoverPresentationController: UIPopoverPresentationController? { return UIAlertController.popoverPresentationController } 45 | #endif 46 | 47 | /// Creates and returns a view controller for displaying an alert to the user. 48 | public required init(title: String?, message: String? = nil, preferredStyle: UIAlertControllerStyle = .alert) { 49 | UIAlertController = UIKit.UIAlertController(title: title, message: message, preferredStyle: preferredStyle) 50 | } 51 | 52 | /// Attaches an action title to the alert or action sheet. 53 | public func addActionWithTitle(title: String, style: UIAlertActionStyle = .default) -> UIAlertAction { 54 | let action = UIAlertAction(title: title, style: style) { action in 55 | if style != .cancel { 56 | self.fulfill(action) 57 | } else { 58 | self.reject(Error.cancelled) 59 | } 60 | } 61 | UIAlertController.addAction(action) 62 | return action 63 | } 64 | 65 | /// Adds a text field to an alert. 66 | public func addTextFieldWithConfigurationHandler(configurationHandler: ((UITextField) -> Void)?) { 67 | UIAlertController.addTextField(configurationHandler: configurationHandler) 68 | } 69 | 70 | fileprivate let UIAlertController: UIKit.UIAlertController 71 | fileprivate let (promise, fulfill, reject) = Promise.pending() 72 | fileprivate var retainCycle: PMKAlertController? 73 | 74 | /// Errors that represent PMKAlertController failures 75 | public enum Error: CancellableError { 76 | /// The user cancelled the PMKAlertController. 77 | case cancelled 78 | 79 | /// - Returns: true 80 | public var isCancelled: Bool { 81 | return self == .cancelled 82 | } 83 | } 84 | } 85 | 86 | extension UIViewController { 87 | /// Presents the PMKAlertController, resolving with the user action. 88 | public func promise(_ vc: PMKAlertController, animated: Bool = true, completion: (() -> Void)? = nil) -> Promise { 89 | vc.retainCycle = vc 90 | present(vc.UIAlertController, animated: animated, completion: completion) 91 | _ = vc.promise.always { _ -> Void in 92 | vc.retainCycle = nil 93 | } 94 | return vc.promise 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /Pods/PromiseKit/Sources/PMKCallVariadicBlock.m: -------------------------------------------------------------------------------- 1 | #import "NSMethodSignatureForBlock.m" 2 | #import 3 | #import 4 | #import "AnyPromise+Private.h" 5 | #import 6 | #import 7 | #import 8 | 9 | #ifndef PMKLog 10 | #define PMKLog NSLog 11 | #endif 12 | 13 | @interface PMKArray : NSObject { 14 | @public 15 | id objs[3]; 16 | NSUInteger count; 17 | } @end 18 | 19 | @implementation PMKArray 20 | 21 | - (id)objectAtIndexedSubscript:(NSUInteger)idx { 22 | if (count <= idx) { 23 | // this check is necessary due to lack of checks in `pmk_safely_call_block` 24 | return nil; 25 | } 26 | return objs[idx]; 27 | } 28 | 29 | @end 30 | 31 | id __PMKArrayWithCount(NSUInteger count, ...) { 32 | PMKArray *this = [PMKArray new]; 33 | this->count = count; 34 | va_list args; 35 | va_start(args, count); 36 | for (NSUInteger x = 0; x < count; ++x) 37 | this->objs[x] = va_arg(args, id); 38 | va_end(args); 39 | return this; 40 | } 41 | 42 | 43 | static inline id _PMKCallVariadicBlock(id frock, id result) { 44 | NSCAssert(frock, @""); 45 | 46 | NSMethodSignature *sig = NSMethodSignatureForBlock(frock); 47 | const NSUInteger nargs = sig.numberOfArguments; 48 | const char rtype = sig.methodReturnType[0]; 49 | 50 | #define call_block_with_rtype(type) ({^type{ \ 51 | switch (nargs) { \ 52 | case 1: \ 53 | return ((type(^)(void))frock)(); \ 54 | case 2: { \ 55 | const id arg = [result class] == [PMKArray class] ? result[0] : result; \ 56 | return ((type(^)(id))frock)(arg); \ 57 | } \ 58 | case 3: { \ 59 | type (^block)(id, id) = frock; \ 60 | return [result class] == [PMKArray class] \ 61 | ? block(result[0], result[1]) \ 62 | : block(result, nil); \ 63 | } \ 64 | case 4: { \ 65 | type (^block)(id, id, id) = frock; \ 66 | return [result class] == [PMKArray class] \ 67 | ? block(result[0], result[1], result[2]) \ 68 | : block(result, nil, nil); \ 69 | } \ 70 | default: \ 71 | @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"PromiseKit: The provided block’s argument count is unsupported." userInfo:nil]; \ 72 | }}();}) 73 | 74 | switch (rtype) { 75 | case 'v': 76 | call_block_with_rtype(void); 77 | return nil; 78 | case '@': 79 | return call_block_with_rtype(id) ?: nil; 80 | case '*': { 81 | char *str = call_block_with_rtype(char *); 82 | return str ? @(str) : nil; 83 | } 84 | case 'c': return @(call_block_with_rtype(char)); 85 | case 'i': return @(call_block_with_rtype(int)); 86 | case 's': return @(call_block_with_rtype(short)); 87 | case 'l': return @(call_block_with_rtype(long)); 88 | case 'q': return @(call_block_with_rtype(long long)); 89 | case 'C': return @(call_block_with_rtype(unsigned char)); 90 | case 'I': return @(call_block_with_rtype(unsigned int)); 91 | case 'S': return @(call_block_with_rtype(unsigned short)); 92 | case 'L': return @(call_block_with_rtype(unsigned long)); 93 | case 'Q': return @(call_block_with_rtype(unsigned long long)); 94 | case 'f': return @(call_block_with_rtype(float)); 95 | case 'd': return @(call_block_with_rtype(double)); 96 | case 'B': return @(call_block_with_rtype(_Bool)); 97 | case '^': 98 | if (strcmp(sig.methodReturnType, "^v") == 0) { 99 | call_block_with_rtype(void); 100 | return nil; 101 | } 102 | // else fall through! 103 | default: 104 | @throw [NSException exceptionWithName:@"PromiseKit" reason:@"PromiseKit: Unsupported method signature." userInfo:nil]; 105 | } 106 | } 107 | 108 | static id PMKCallVariadicBlock(id frock, id result) { 109 | @try { 110 | return _PMKCallVariadicBlock(frock, result); 111 | } @catch (id thrown) { 112 | return PMKProcessUnhandledException(thrown); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /Pods/PromiseKit/Extensions/UIKit/Sources/UIViewController+Promise.swift: -------------------------------------------------------------------------------- 1 | import Foundation.NSError 2 | import UIKit 3 | #if !COCOAPODS 4 | import PromiseKit 5 | #endif 6 | 7 | /** 8 | To import this `UIViewController` category: 9 | 10 | use_frameworks! 11 | pod "PromiseKit/UIKit" 12 | 13 | Or `UIKit` is one of the categories imported by the umbrella pod: 14 | 15 | use_frameworks! 16 | pod "PromiseKit" 17 | 18 | And then in your sources: 19 | 20 | import PromiseKit 21 | */ 22 | extension UIViewController { 23 | 24 | public enum PMKError: Error { 25 | case navigationControllerEmpty 26 | case noImageFound 27 | case notPromisable 28 | case notGenericallyPromisable 29 | case nilPromisable 30 | } 31 | 32 | /// Configures when a UIViewController promise resolves 33 | public enum FulfillmentType { 34 | /// The promise resolves just after the view controller has disappeared. 35 | case onceDisappeared 36 | /// The promise resolves before the view controller has disappeared. 37 | case beforeDismissal 38 | } 39 | 40 | /// Presents the UIViewController, resolving with the user action. 41 | public func promise(_ vc: UIViewController, animate animationOptions: PMKAnimationOptions = [.appear, .disappear], fulfills fulfillmentType: FulfillmentType = .onceDisappeared, completion: (() -> Void)? = nil) -> Promise { 42 | let pvc: UIViewController 43 | 44 | switch vc { 45 | case let nc as UINavigationController: 46 | guard let vc = nc.viewControllers.first else { return Promise(error: PMKError.navigationControllerEmpty) } 47 | pvc = vc 48 | default: 49 | pvc = vc 50 | } 51 | 52 | let promise: Promise 53 | 54 | if !(pvc is Promisable) { 55 | promise = Promise(error: PMKError.notPromisable) 56 | } else if let p = pvc.value(forKeyPath: "promise") as? Promise { 57 | promise = p 58 | } else if let _ = pvc.value(forKeyPath: "promise") { 59 | promise = Promise(error: PMKError.notGenericallyPromisable) 60 | } else { 61 | promise = Promise(error: PMKError.nilPromisable) 62 | } 63 | 64 | if !promise.isPending { 65 | return promise 66 | } 67 | 68 | present(vc, animated: animationOptions.contains(.appear), completion: completion) 69 | 70 | let (wrappingPromise, fulfill, reject) = Promise.pending() 71 | 72 | switch fulfillmentType { 73 | case .onceDisappeared: 74 | promise.then { result in 75 | vc.presentingViewController?.dismiss(animated: animationOptions.contains(.disappear), completion: { fulfill(result) }) 76 | } 77 | .catch(policy: .allErrors) { error in 78 | vc.presentingViewController?.dismiss(animated: animationOptions.contains(.disappear), completion: { reject(error) }) 79 | } 80 | case .beforeDismissal: 81 | promise.then { result -> Void in 82 | fulfill(result) 83 | vc.presentingViewController?.dismiss(animated: animationOptions.contains(.disappear), completion: nil) 84 | } 85 | .catch(policy: .allErrors) { error in 86 | reject(error) 87 | vc.presentingViewController?.dismiss(animated: animationOptions.contains(.disappear), completion: nil) 88 | } 89 | } 90 | 91 | return wrappingPromise 92 | } 93 | 94 | @available(*, deprecated: 3.4, renamed: "promise(_:animate:fulfills:completion:)") 95 | public func promiseViewController(_ vc: UIViewController, animated: Bool = true, completion: (() -> Void)? = nil) -> Promise { 96 | return promise(vc, animate: animated ? [.appear, .disappear] : [], completion: completion) 97 | } 98 | } 99 | 100 | /// A protocol for UIViewControllers that can be promised. 101 | @objc(Promisable) public protocol Promisable { 102 | /** 103 | Provide a promise for promiseViewController here. 104 | 105 | The resulting property must be annotated with @objc. 106 | 107 | Obviously return a Promise. There is an issue with generics and Swift and 108 | protocols currently so we couldn't specify that. 109 | */ 110 | var promise: AnyObject! { get } 111 | } 112 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-viper/Pods-viper-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 5 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 6 | 7 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 8 | 9 | install_framework() 10 | { 11 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 12 | local source="${BUILT_PRODUCTS_DIR}/$1" 13 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 14 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 15 | elif [ -r "$1" ]; then 16 | local source="$1" 17 | fi 18 | 19 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 20 | 21 | if [ -L "${source}" ]; then 22 | echo "Symlinked..." 23 | source="$(readlink "${source}")" 24 | fi 25 | 26 | # use filter instead of exclude so missing patterns dont' throw errors 27 | echo "rsync -av --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 28 | rsync -av --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 29 | 30 | local basename 31 | basename="$(basename -s .framework "$1")" 32 | binary="${destination}/${basename}.framework/${basename}" 33 | if ! [ -r "$binary" ]; then 34 | binary="${destination}/${basename}" 35 | fi 36 | 37 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 38 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 39 | strip_invalid_archs "$binary" 40 | fi 41 | 42 | # Resign the code if required by the build settings to avoid unstable apps 43 | code_sign_if_enabled "${destination}/$(basename "$1")" 44 | 45 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 46 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 47 | local swift_runtime_libs 48 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) 49 | for lib in $swift_runtime_libs; do 50 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 51 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 52 | code_sign_if_enabled "${destination}/${lib}" 53 | done 54 | fi 55 | } 56 | 57 | # Signs a framework with the provided identity 58 | code_sign_if_enabled() { 59 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 60 | # Use the current code_sign_identitiy 61 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 62 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements '$1'" 63 | 64 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 65 | code_sign_cmd="$code_sign_cmd &" 66 | fi 67 | echo "$code_sign_cmd" 68 | eval "$code_sign_cmd" 69 | fi 70 | } 71 | 72 | # Strip invalid architectures 73 | strip_invalid_archs() { 74 | binary="$1" 75 | # Get architectures for current file 76 | archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)" 77 | stripped="" 78 | for arch in $archs; do 79 | if ! [[ "${VALID_ARCHS}" == *"$arch"* ]]; then 80 | # Strip non-valid architectures in-place 81 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1 82 | stripped="$stripped $arch" 83 | fi 84 | done 85 | if [[ "$stripped" ]]; then 86 | echo "Stripped $binary of architectures:$stripped" 87 | fi 88 | } 89 | 90 | 91 | if [[ "$CONFIGURATION" == "Debug" ]]; then 92 | install_framework "$BUILT_PRODUCTS_DIR/Alamofire/Alamofire.framework" 93 | install_framework "$BUILT_PRODUCTS_DIR/Compass/Compass.framework" 94 | install_framework "$BUILT_PRODUCTS_DIR/ObjectMapper/ObjectMapper.framework" 95 | install_framework "$BUILT_PRODUCTS_DIR/PromiseKit/PromiseKit.framework" 96 | fi 97 | if [[ "$CONFIGURATION" == "Release" ]]; then 98 | install_framework "$BUILT_PRODUCTS_DIR/Alamofire/Alamofire.framework" 99 | install_framework "$BUILT_PRODUCTS_DIR/Compass/Compass.framework" 100 | install_framework "$BUILT_PRODUCTS_DIR/ObjectMapper/ObjectMapper.framework" 101 | install_framework "$BUILT_PRODUCTS_DIR/PromiseKit/PromiseKit.framework" 102 | fi 103 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 104 | wait 105 | fi 106 | -------------------------------------------------------------------------------- /Pods/PromiseKit/Extensions/Foundation/Sources/Process+Promise.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | #if !COCOAPODS 3 | import PromiseKit 4 | #endif 5 | 6 | #if os(macOS) 7 | 8 | /** 9 | To import the `Process` category: 10 | 11 | use_frameworks! 12 | pod "PromiseKit/Foundation" 13 | 14 | Or `Process` is one of the categories imported by the umbrella pod: 15 | 16 | use_frameworks! 17 | pod "PromiseKit" 18 | 19 | And then in your sources: 20 | 21 | import PromiseKit 22 | */ 23 | extension Process { 24 | /** 25 | Launches the receiver and resolves when it exits. 26 | 27 | let proc = Process() 28 | proc.launchPath = "/bin/ls" 29 | proc.arguments = ["/bin"] 30 | proc.promise().asStandardOutput(encoding: .utf8).then { str in 31 | print(str) 32 | } 33 | */ 34 | public func promise() -> ProcessPromise { 35 | standardOutput = Pipe() 36 | standardError = Pipe() 37 | 38 | launch() 39 | 40 | let (p, fulfill, reject) = ProcessPromise.pending() 41 | let promise = p as! ProcessPromise 42 | 43 | promise.task = self 44 | 45 | waldo.async { 46 | self.waitUntilExit() 47 | 48 | if self.terminationReason == .exit && self.terminationStatus == 0 { 49 | fulfill() 50 | } else { 51 | reject(Error(.execution, promise, self)) 52 | } 53 | 54 | promise.task = nil 55 | } 56 | 57 | return promise 58 | } 59 | 60 | /** 61 | The error generated by PromiseKit’s `Process` extension 62 | */ 63 | public struct Error: Swift.Error, CustomStringConvertible { 64 | public let exitStatus: Int 65 | public let stdout: Data 66 | public let stderr: Data 67 | public let args: [String] 68 | public let code: Code 69 | public let cmd: String 70 | 71 | init(_ code: Code, _ promise: ProcessPromise, _ task: Process) { 72 | stdout = promise.stdout 73 | stderr = promise.stderr 74 | exitStatus = Int(task.terminationStatus) 75 | cmd = task.launchPath ?? "" 76 | args = task.arguments ?? [] 77 | self.code = code 78 | } 79 | 80 | /// The type of `Process` error 81 | public enum Code { 82 | /// The data could not be converted to a UTF8 String 83 | case encoding 84 | /// The task execution failed 85 | case execution 86 | } 87 | 88 | /// A textual representation of the error 89 | public var description: String { 90 | switch code { 91 | case .encoding: 92 | return "Could not decode command output into string." 93 | case .execution: 94 | let str = ([cmd] + args).joined(separator: " ") 95 | return "Failed executing: `\(str)`." 96 | } 97 | } 98 | } 99 | } 100 | 101 | final public class ProcessPromise: Promise { 102 | fileprivate var task: Process! 103 | 104 | fileprivate var stdout: Data { 105 | return (task.standardOutput! as! Pipe).fileHandleForReading.readDataToEndOfFile() 106 | } 107 | 108 | fileprivate var stderr: Data { 109 | return (task.standardError! as! Pipe).fileHandleForReading.readDataToEndOfFile() 110 | } 111 | 112 | public func asStandardOutput() -> Promise { 113 | return then(on: zalgo) { _ in self.stdout } 114 | } 115 | 116 | public func asStandardError() -> Promise { 117 | return then(on: zalgo) { _ in self.stderr } 118 | } 119 | 120 | public func asStandardPair() -> Promise<(Data, Data)> { 121 | return then(on: zalgo) { _ in (self.stderr, self.stdout) } 122 | } 123 | 124 | private func decode(_ encoding: String.Encoding, _ data: Data) throws -> String { 125 | guard let str = String(bytes: data, encoding: encoding) else { 126 | throw Process.Error(.encoding, self, self.task) 127 | } 128 | return str 129 | } 130 | 131 | public func asStandardPair(encoding: String.Encoding) -> Promise<(String, String)> { 132 | return then(on: zalgo) { _ in 133 | (try self.decode(encoding, self.stdout), try self.decode(encoding, self.stderr)) 134 | } 135 | } 136 | 137 | public func asStandardOutput(encoding: String.Encoding) -> Promise { 138 | return then(on: zalgo) { _ in try self.decode(encoding, self.stdout) } 139 | } 140 | 141 | public func asStandardError(encoding: String.Encoding) -> Promise { 142 | return then(on: zalgo) { _ in try self.decode(encoding, self.stderr) } 143 | } 144 | } 145 | 146 | #endif 147 | -------------------------------------------------------------------------------- /Pods/PromiseKit/Sources/when.m: -------------------------------------------------------------------------------- 1 | @import Foundation.NSDictionary; 2 | #import "AnyPromise+Private.h" 3 | @import Foundation.NSProgress; 4 | #import 5 | @import Foundation.NSError; 6 | @import Foundation.NSNull; 7 | #import "PromiseKit.h" 8 | 9 | // NSProgress resources: 10 | // * https://robots.thoughtbot.com/asynchronous-nsprogress 11 | // * http://oleb.net/blog/2014/03/nsprogress/ 12 | // NSProgress! Beware! 13 | // * https://github.com/AFNetworking/AFNetworking/issues/2261 14 | 15 | /** 16 | Wait for all promises in a set to resolve. 17 | 18 | @note If *any* of the provided promises reject, the returned promise is immediately rejected with that error. 19 | @warning In the event of rejection the other promises will continue to resolve and, as per any other promise, will either fulfill or reject. This is the right pattern for `getter` style asynchronous tasks, but often for `setter` tasks (eg. storing data on a server), you most likely will need to wait on all tasks and then act based on which have succeeded and which have failed, in such situations use `when(resolved:)`. 20 | @param promises The promises upon which to wait before the returned promise resolves. 21 | @note PMKWhen provides NSProgress. 22 | @return A new promise that resolves when all the provided promises fulfill or one of the provided promises rejects. 23 | */ 24 | AnyPromise *PMKWhen(id promises) { 25 | if (promises == nil) 26 | return [AnyPromise promiseWithValue:[NSError errorWithDomain:PMKErrorDomain code:PMKInvalidUsageError userInfo:@{NSLocalizedDescriptionKey: @"PMKWhen(nil)"}]]; 27 | 28 | if ([promises isKindOfClass:[NSArray class]] || [promises isKindOfClass:[NSDictionary class]]) { 29 | if ([promises count] == 0) 30 | return [AnyPromise promiseWithValue:promises]; 31 | } else if ([promises isKindOfClass:[AnyPromise class]]) { 32 | promises = @[promises]; 33 | } else { 34 | return [AnyPromise promiseWithValue:promises]; 35 | } 36 | 37 | #ifndef PMKDisableProgress 38 | NSProgress *progress = [NSProgress progressWithTotalUnitCount:(int64_t)[promises count]]; 39 | progress.pausable = NO; 40 | progress.cancellable = NO; 41 | #else 42 | struct PMKProgress { 43 | int completedUnitCount; 44 | int totalUnitCount; 45 | double fractionCompleted; 46 | }; 47 | __block struct PMKProgress progress; 48 | #endif 49 | 50 | __block int32_t countdown = (int32_t)[promises count]; 51 | BOOL const isdict = [promises isKindOfClass:[NSDictionary class]]; 52 | 53 | return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) { 54 | NSInteger index = 0; 55 | 56 | for (__strong id key in promises) { 57 | AnyPromise *promise = isdict ? promises[key] : key; 58 | if (!isdict) key = @(index); 59 | 60 | if (![promise isKindOfClass:[AnyPromise class]]) 61 | promise = [AnyPromise promiseWithValue:promise]; 62 | 63 | [promise __pipe:^(id value){ 64 | if (progress.fractionCompleted >= 1) 65 | return; 66 | 67 | if (IsError(value)) { 68 | progress.completedUnitCount = progress.totalUnitCount; 69 | 70 | NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithDictionary:[value userInfo] ?: @{}]; 71 | userInfo[PMKFailingPromiseIndexKey] = key; 72 | [userInfo setObject:value forKey:NSUnderlyingErrorKey]; 73 | id err = [[NSError alloc] initWithDomain:[value domain] code:[value code] userInfo:userInfo]; 74 | resolve(err); 75 | } 76 | else if (OSAtomicDecrement32(&countdown) == 0) { 77 | progress.completedUnitCount = progress.totalUnitCount; 78 | 79 | id results; 80 | if (isdict) { 81 | results = [NSMutableDictionary new]; 82 | for (id key in promises) { 83 | id promise = promises[key]; 84 | results[key] = IsPromise(promise) ? ((AnyPromise *)promise).value : promise; 85 | } 86 | } else { 87 | results = [NSMutableArray new]; 88 | for (AnyPromise *promise in promises) { 89 | id value = IsPromise(promise) ? (promise.value ?: [NSNull null]) : promise; 90 | [results addObject:value]; 91 | } 92 | } 93 | resolve(results); 94 | } else { 95 | progress.completedUnitCount++; 96 | } 97 | }]; 98 | } 99 | }]; 100 | } 101 | --------------------------------------------------------------------------------