├── .DS_Store ├── .gitattributes ├── .gitignore ├── CleanArchitectureWithCoordinatorPatternDemo.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── CleanArchitectureWithCoordinatorPatternDemo.xcworkspace └── contents.xcworkspacedata ├── CleanArchitectureWithCoordinatorPatternDemo ├── AppCoordinator.swift ├── AppDelegate.swift ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Contents.json │ ├── art1.imageset │ │ ├── Contents.json │ │ └── f9714f9bf554ff24b06c978aea9a6a25--marvel-art-marvel-heroes.jpg │ ├── art2.imageset │ │ ├── Contents.json │ │ └── art2.jpg │ ├── art3.imageset │ │ ├── Contents.json │ │ └── art3.jpg │ ├── background.imageset │ │ ├── Contents.json │ │ └── fa252277c39aa2f722fd9d5ce2bfccce--man-wallpaper-ipod-wallpaper.jpg │ ├── enter.imageset │ │ ├── Contents.json │ │ └── enter.jpg │ └── splash.imageset │ │ ├── Contents.json │ │ └── splash.jpg ├── Info.plist ├── Resources │ └── Storyboards │ │ ├── Auth.storyboard │ │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ │ └── Onboarding.storyboard └── Source │ ├── Common │ ├── Base │ │ └── BaseCoordinator.swift │ ├── Bridging-Headers │ │ └── CommonCrypto-Bridging-Header.h │ ├── Extensions │ │ ├── IndexPath+Helper.swift │ │ ├── NSObject+ClassDefinition.swift │ │ ├── String+md5.swift │ │ ├── UIAlertController+Helper.swift │ │ ├── UICollectionView+Nib.swift │ │ └── UIViewController+Creation.swift │ ├── Factories │ │ ├── Coordinator │ │ │ ├── CoordinatorFactory.swift │ │ │ └── CoordinatorFactoryProtocol.swift │ │ └── Modules │ │ │ ├── Authorization │ │ │ └── AuthorizationFactoryProtocol.swift │ │ │ ├── Main │ │ │ └── MainFactoryProtocol.swift │ │ │ ├── ModulesFactory.swift │ │ │ └── Onboarding │ │ │ └── OnboardingFactoryProtocol.swift │ ├── Helpers │ │ ├── Constants.swift │ │ ├── RequestWrapper.swift │ │ ├── Router.swift │ │ ├── Session.swift │ │ └── UserDefaultsWrapper.swift │ ├── Managers │ │ └── Networking │ │ │ ├── NSError+Creation.swift │ │ │ ├── RequestManager.swift │ │ │ ├── RequestManagerConfiguration.swift │ │ │ └── RequestManagerErroring.swift │ ├── Model │ │ ├── Character.swift │ │ ├── Comics.swift │ │ ├── ComicsItem.swift │ │ ├── Events.swift │ │ ├── EventsItem.swift │ │ ├── Series.swift │ │ ├── SeriesItem.swift │ │ ├── Stories.swift │ │ ├── StoriesItem.swift │ │ ├── Thumbnail.swift │ │ └── URLS.swift │ └── Protocols │ │ ├── BaseViewProtocol.swift │ │ ├── Coordinatable.swift │ │ ├── Presentable.swift │ │ └── Routable.swift │ └── Modules │ └── Flows │ ├── Authorization │ ├── AuthorizationCoordinator.swift │ ├── AuthorizationCoordinatorOutput.swift │ └── Controllers │ │ ├── Enter │ │ ├── EnterAssembly.swift │ │ ├── EnterInteractor.swift │ │ ├── EnterPresenter.swift │ │ └── EnterViewController.swift │ │ ├── EnterAssembly.swift │ │ ├── EnterInteractor.swift │ │ ├── EnterPresenter.swift │ │ ├── EnterViewController.swift │ │ └── EnterViewProtocol.swift │ ├── Main │ ├── Controllers │ │ ├── Cells │ │ │ └── CharacterCollectionViewCell.swift │ │ └── Characters │ │ │ ├── CharactersAssembly.swift │ │ │ ├── CharactersDataSource.swift │ │ │ ├── CharactersInteractor.swift │ │ │ ├── CharactersPresenter.swift │ │ │ ├── CharactersViewController.swift │ │ │ └── CharactersViewProtocol.swift │ ├── MainCoordinator.swift │ ├── MainCoordinatorOutput.swift │ └── MainRequestManager.swift │ └── Onboarding │ ├── Controllers │ ├── OnboardingPageViewController.swift │ ├── OnboardingViewController.swift │ └── OnboardingViewProtocol.swift │ ├── OnboardingCoordinator.swift │ └── OnboardingCoordinatorOutput.swift ├── LICENSE ├── Module VIP.xctemplate ├── .DS_Store ├── TemplateIcon.icns ├── TemplateInfo.plist ├── ___FILEBASENAME___Assembly.swift ├── ___FILEBASENAME___Interactor.swift ├── ___FILEBASENAME___Presenter.swift ├── ___FILEBASENAME___ViewController.swift └── ___FILEBASENAME___ViewProtocol.swift ├── Podfile ├── Podfile.lock ├── Pods ├── Alamofire │ ├── LICENSE │ ├── README.md │ └── Source │ │ ├── AFError.swift │ │ ├── Alamofire.swift │ │ ├── DispatchQueue+Alamofire.swift │ │ ├── MultipartFormData.swift │ │ ├── NetworkReachabilityManager.swift │ │ ├── Notifications.swift │ │ ├── ParameterEncoding.swift │ │ ├── Request.swift │ │ ├── Response.swift │ │ ├── ResponseSerialization.swift │ │ ├── Result.swift │ │ ├── ServerTrustPolicy.swift │ │ ├── SessionDelegate.swift │ │ ├── SessionManager.swift │ │ ├── TaskDelegate.swift │ │ ├── Timeline.swift │ │ └── Validation.swift ├── AlamofireObjectMapper │ ├── AlamofireObjectMapper │ │ └── AlamofireObjectMapper.swift │ ├── LICENSE │ └── README.md ├── Kingfisher │ ├── LICENSE │ ├── README.md │ └── Sources │ │ ├── AnimatedImageView.swift │ │ ├── Box.swift │ │ ├── CacheSerializer.swift │ │ ├── Filter.swift │ │ ├── FormatIndicatedCacheSerializer.swift │ │ ├── Image.swift │ │ ├── ImageCache.swift │ │ ├── ImageDownloader.swift │ │ ├── ImagePrefetcher.swift │ │ ├── ImageProcessor.swift │ │ ├── ImageTransition.swift │ │ ├── ImageView+Kingfisher.swift │ │ ├── Indicator.swift │ │ ├── Kingfisher.h │ │ ├── Kingfisher.swift │ │ ├── KingfisherManager.swift │ │ ├── KingfisherOptionsInfo.swift │ │ ├── Placeholder.swift │ │ ├── RequestModifier.swift │ │ ├── Resource.swift │ │ ├── String+MD5.swift │ │ ├── ThreadHelper.swift │ │ └── UIButton+Kingfisher.swift ├── Manifest.lock ├── ObjectMapper │ ├── LICENSE │ ├── README-CN.md │ └── Sources │ │ ├── CustomDateFormatTransform.swift │ │ ├── DataTransform.swift │ │ ├── DateFormatterTransform.swift │ │ ├── DateTransform.swift │ │ ├── DictionaryTransform.swift │ │ ├── EnumOperators.swift │ │ ├── EnumTransform.swift │ │ ├── FromJSON.swift │ │ ├── HexColorTransform.swift │ │ ├── ISO8601DateTransform.swift │ │ ├── ImmutableMappable.swift │ │ ├── IntegerOperators.swift │ │ ├── Map.swift │ │ ├── MapError.swift │ │ ├── Mappable.swift │ │ ├── Mapper.swift │ │ ├── NSDecimalNumberTransform.swift │ │ ├── Operators.swift │ │ ├── ToJSON.swift │ │ ├── TransformOf.swift │ │ ├── TransformOperators.swift │ │ ├── TransformType.swift │ │ └── URLTransform.swift ├── Pods.xcodeproj │ └── project.pbxproj └── Target Support Files │ ├── Alamofire │ ├── Alamofire-dummy.m │ ├── Alamofire-prefix.pch │ ├── Alamofire-umbrella.h │ ├── Alamofire.modulemap │ ├── Alamofire.xcconfig │ └── Info.plist │ ├── AlamofireObjectMapper │ ├── AlamofireObjectMapper-dummy.m │ ├── AlamofireObjectMapper-prefix.pch │ ├── AlamofireObjectMapper-umbrella.h │ ├── AlamofireObjectMapper.modulemap │ ├── AlamofireObjectMapper.xcconfig │ └── Info.plist │ ├── Kingfisher │ ├── Info.plist │ ├── Kingfisher-dummy.m │ ├── Kingfisher-prefix.pch │ ├── Kingfisher-umbrella.h │ ├── Kingfisher.modulemap │ └── Kingfisher.xcconfig │ ├── ObjectMapper │ ├── Info.plist │ ├── ObjectMapper-dummy.m │ ├── ObjectMapper-prefix.pch │ ├── ObjectMapper-umbrella.h │ ├── ObjectMapper.modulemap │ └── ObjectMapper.xcconfig │ └── Pods-CleanArchitectureWithCoordinatorPatternDemo │ ├── Info.plist │ ├── Pods-CleanArchitectureWithCoordinatorPatternDemo-acknowledgements.markdown │ ├── Pods-CleanArchitectureWithCoordinatorPatternDemo-acknowledgements.plist │ ├── Pods-CleanArchitectureWithCoordinatorPatternDemo-dummy.m │ ├── Pods-CleanArchitectureWithCoordinatorPatternDemo-frameworks.sh │ ├── Pods-CleanArchitectureWithCoordinatorPatternDemo-resources.sh │ ├── Pods-CleanArchitectureWithCoordinatorPatternDemo-umbrella.h │ ├── Pods-CleanArchitectureWithCoordinatorPatternDemo.debug.xcconfig │ ├── Pods-CleanArchitectureWithCoordinatorPatternDemo.modulemap │ └── Pods-CleanArchitectureWithCoordinatorPatternDemo.release.xcconfig ├── README.md ├── install.swift └── uninstall.swift /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaksimKazachkov/iOS-Clean-Architecture-with-Coordinator-pattern/02c5f4ede8dc901e77dbd27eed0e55c858316047/.DS_Store -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xccheckout 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | *.dSYM.zip 29 | *.dSYM 30 | 31 | ## Playgrounds 32 | timeline.xctimeline 33 | playground.xcworkspace 34 | 35 | # Swift Package Manager 36 | # 37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 38 | # Packages/ 39 | # Package.pins 40 | .build/ 41 | 42 | # CocoaPods 43 | # 44 | # We recommend against adding the Pods directory to your .gitignore. However 45 | # you should judge for yourself, the pros and cons are mentioned at: 46 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 47 | # 48 | # Pods/ 49 | 50 | # Carthage 51 | # 52 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 53 | # Carthage/Checkouts 54 | 55 | Carthage/Build 56 | 57 | # fastlane 58 | # 59 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 60 | # screenshots whenever they are needed. 61 | # For more information about the recommended setup visit: 62 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 63 | 64 | fastlane/report.xml 65 | fastlane/Preview.html 66 | fastlane/screenshots 67 | fastlane/test_output 68 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/AppCoordinator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppCoordinator.swift 3 | // CleanArchitectureWithCoordinatorPatternDemo 4 | // 5 | // Created by Maksim Kazachkov on 06.11.2017. 6 | // Copyright © 2017 Maksim Kazachkov. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | fileprivate enum LaunchInstructor { 12 | case onboarding, authorization, main 13 | 14 | static func setup() -> LaunchInstructor { 15 | switch (Session.isSeenOnboarding, Session.isAuthorized) { 16 | case (false, false), (true, false): 17 | return .authorization 18 | case (false, true): 19 | return .onboarding 20 | case (true, true): 21 | return .main 22 | } 23 | } 24 | } 25 | 26 | final class AppCoordinator: BaseCoordinator { 27 | 28 | fileprivate let factory: CoordinatorFactoryProtocol 29 | fileprivate let router : Routable 30 | 31 | fileprivate var instructor: LaunchInstructor { 32 | return LaunchInstructor.setup() 33 | } 34 | 35 | init(router: Routable, factory: CoordinatorFactory) { 36 | self.router = router 37 | self.factory = factory 38 | } 39 | } 40 | 41 | // MARK:- Coordinatable 42 | extension AppCoordinator: Coordinatable { 43 | func start() { 44 | switch instructor { 45 | case .authorization: 46 | performAuthorizationFlow() 47 | case .onboarding: 48 | performOnboarding() 49 | case .main: 50 | performMainFlow() 51 | } 52 | } 53 | } 54 | 55 | // MARK:- Private methods 56 | private extension AppCoordinator { 57 | func performAuthorizationFlow() { 58 | let coordinator = factory.makeAuthorizationCoordinator(router: router) 59 | coordinator.finishFlow = { [unowned self, unowned coordinator] in 60 | self.removeDependency(coordinator) 61 | self.start() 62 | } 63 | addDependency(coordinator) 64 | coordinator.start() 65 | } 66 | 67 | func performMainFlow() { 68 | let coordinator = factory.makeMainCoordinator(router: router) 69 | coordinator.finishFlow = { [unowned self, unowned coordinator] in 70 | self.start() 71 | self.removeDependency(coordinator) 72 | } 73 | addDependency(coordinator) 74 | coordinator.start() 75 | } 76 | 77 | func performOnboarding() { 78 | let coordinator = factory.makeOnboardingCoordinator(router: router) 79 | coordinator.finishFlow = { [weak self, weak coordinator] in 80 | guard 81 | let `self` = self, 82 | let `coordinator` = coordinator 83 | else { return } 84 | Session.isSeenOnboarding = true 85 | self.start() 86 | self.removeDependency(coordinator) 87 | } 88 | addDependency(coordinator) 89 | coordinator.start() 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // CleanArchitectureWithCoordinatorPatternDemo 4 | // 5 | // Created by Maksim Kazachkov on 06.11.2017. 6 | // Copyright © 2017 Maksim Kazachkov. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | var rootController: UINavigationController { 17 | window?.rootViewController = UINavigationController() 18 | return window?.rootViewController as! UINavigationController 19 | } 20 | 21 | fileprivate lazy var coordinator: Coordinatable = self.makeCoordinator() 22 | 23 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 24 | // Override point for customization after application launch. 25 | 26 | coordinator.start() 27 | return true 28 | } 29 | } 30 | 31 | // MARK:- Private methods 32 | private extension AppDelegate { 33 | func makeCoordinator() -> Coordinatable { 34 | return AppCoordinator(router: Router(rootController: rootController), 35 | factory: CoordinatorFactory()) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/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 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Assets.xcassets/art1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "f9714f9bf554ff24b06c978aea9a6a25--marvel-art-marvel-heroes.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Assets.xcassets/art1.imageset/f9714f9bf554ff24b06c978aea9a6a25--marvel-art-marvel-heroes.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaksimKazachkov/iOS-Clean-Architecture-with-Coordinator-pattern/02c5f4ede8dc901e77dbd27eed0e55c858316047/CleanArchitectureWithCoordinatorPatternDemo/Assets.xcassets/art1.imageset/f9714f9bf554ff24b06c978aea9a6a25--marvel-art-marvel-heroes.jpg -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Assets.xcassets/art2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "art2.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Assets.xcassets/art2.imageset/art2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaksimKazachkov/iOS-Clean-Architecture-with-Coordinator-pattern/02c5f4ede8dc901e77dbd27eed0e55c858316047/CleanArchitectureWithCoordinatorPatternDemo/Assets.xcassets/art2.imageset/art2.jpg -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Assets.xcassets/art3.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "art3.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Assets.xcassets/art3.imageset/art3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaksimKazachkov/iOS-Clean-Architecture-with-Coordinator-pattern/02c5f4ede8dc901e77dbd27eed0e55c858316047/CleanArchitectureWithCoordinatorPatternDemo/Assets.xcassets/art3.imageset/art3.jpg -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Assets.xcassets/background.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "fa252277c39aa2f722fd9d5ce2bfccce--man-wallpaper-ipod-wallpaper.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Assets.xcassets/background.imageset/fa252277c39aa2f722fd9d5ce2bfccce--man-wallpaper-ipod-wallpaper.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaksimKazachkov/iOS-Clean-Architecture-with-Coordinator-pattern/02c5f4ede8dc901e77dbd27eed0e55c858316047/CleanArchitectureWithCoordinatorPatternDemo/Assets.xcassets/background.imageset/fa252277c39aa2f722fd9d5ce2bfccce--man-wallpaper-ipod-wallpaper.jpg -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Assets.xcassets/enter.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "enter.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Assets.xcassets/enter.imageset/enter.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaksimKazachkov/iOS-Clean-Architecture-with-Coordinator-pattern/02c5f4ede8dc901e77dbd27eed0e55c858316047/CleanArchitectureWithCoordinatorPatternDemo/Assets.xcassets/enter.imageset/enter.jpg -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Assets.xcassets/splash.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "splash.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Assets.xcassets/splash.imageset/splash.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaksimKazachkov/iOS-Clean-Architecture-with-Coordinator-pattern/02c5f4ede8dc901e77dbd27eed0e55c858316047/CleanArchitectureWithCoordinatorPatternDemo/Assets.xcassets/splash.imageset/splash.jpg -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 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 | NSAppTransportSecurity 24 | 25 | NSAllowsArbitraryLoads 26 | 27 | 28 | UILaunchStoryboardName 29 | LaunchScreen 30 | UIMainStoryboardFile 31 | Main 32 | UIRequiredDeviceCapabilities 33 | 34 | armv7 35 | 36 | UISupportedInterfaceOrientations 37 | 38 | UIInterfaceOrientationPortrait 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UISupportedInterfaceOrientations~ipad 43 | 44 | UIInterfaceOrientationPortrait 45 | UIInterfaceOrientationPortraitUpsideDown 46 | UIInterfaceOrientationLandscapeLeft 47 | UIInterfaceOrientationLandscapeRight 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Resources/Storyboards/Auth.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | Georgia 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Resources/Storyboards/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 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Source/Common/Base/BaseCoordinator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BaseCoordinator.swift 3 | // Worker Dashy 4 | // 5 | // Created by Maksim Kazachkov on 16.08.17. 6 | // Copyright © 2017 Umbrella. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class BaseCoordinator { 12 | 13 | var childCoordinators: [Coordinatable] = [] 14 | 15 | // Add only unique object 16 | 17 | func addDependency(_ coordinator: Coordinatable) { 18 | for element in childCoordinators { 19 | if element === coordinator { return } 20 | } 21 | childCoordinators.append(coordinator) 22 | } 23 | 24 | func removeDependency(_ coordinator: Coordinatable?) { 25 | guard 26 | childCoordinators.isEmpty == false, 27 | let coordinator = coordinator 28 | else { return } 29 | 30 | for (index, element) in childCoordinators.enumerated() { 31 | if element === coordinator { 32 | childCoordinators.remove(at: index) 33 | break 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Source/Common/Bridging-Headers/CommonCrypto-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // CommonCrypto-Bridging-Header.h 3 | // Marvel 4 | // 5 | // Created by Максим on 05.09.16. 6 | // Copyright © 2016 Максим. All rights reserved. 7 | // 8 | 9 | #ifndef CommonCrypto_Bridging_Header_h 10 | #define CommonCrypto_Bridging_Header_h 11 | 12 | 13 | #endif /* CommonCrypto_Bridging_Header_h */ 14 | 15 | #import -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Source/Common/Extensions/IndexPath+Helper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // IndexPath+Helper.swift 3 | // Worker Dashy 4 | // 5 | // Created by Maksim Kazachkov on 28.08.17. 6 | // Copyright © 2017 Umbrella. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension IndexPath { 12 | static func configure(for anyValues: [T], with previusValues: [V], section: Int) -> [IndexPath] { 13 | let from = !previusValues.isEmpty ? previusValues.count - anyValues.count : 0 14 | let newIndexPaths: [IndexPath] = (from...previusValues.count - 1).map { IndexPath(item: $0, section: section) } 15 | return newIndexPaths 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Source/Common/Extensions/NSObject+ClassDefinition.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+ClassDefinition.swift 3 | // Worker Dashy 4 | // 5 | // Created by Maksim Kazachkov on 16.08.17. 6 | // Copyright © 2017 Umbrella. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension NSObject { 12 | static func nameOfClass() -> String { 13 | return NSStringFromClass(self).components(separatedBy: ".").last! 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Source/Common/Extensions/String+md5.swift: -------------------------------------------------------------------------------- 1 | // 2 | // String+md5.swift 3 | // Marvel 4 | // 5 | // Created by Максим on 05.09.16. 6 | // Copyright © 2016 Максим. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension String { 12 | func md5() -> String! { 13 | let str = self.cString(using: String.Encoding.utf8) 14 | let strLen = CUnsignedInt(self.lengthOfBytes(using: String.Encoding.utf8)) 15 | let digestLen = Int(CC_MD5_DIGEST_LENGTH) 16 | let result = UnsafeMutablePointer.allocate(capacity: digestLen) 17 | 18 | CC_MD5(str!, strLen, result) 19 | 20 | let hash = NSMutableString() 21 | for i in 0.. Void)? = nil) 18 | { 19 | let alert = UIAlertController(title: title, message: message, preferredStyle: .alert) 20 | 21 | let alertActionOk = UIAlertAction(title: "OK", style: .default) { action in 22 | alert.dismiss(animated: true, completion: nil) 23 | if let actionBlock = actionBlock { 24 | actionBlock() 25 | } 26 | } 27 | 28 | alert.addAction(alertActionOk) 29 | DispatchQueue.main.async(execute: { 30 | viewController.present(alert, animated: true, completion: nil) 31 | if tintColor != nil { 32 | alert.view.tintColor = tintColor! 33 | } 34 | }) 35 | } 36 | 37 | public static func showActionSheet( 38 | title: String?, 39 | message: String?, 40 | viewController: UIViewController, 41 | actions: [UIAlertAction]?, 42 | cancelTitle: String, 43 | tintColor: UIColor?) 44 | { 45 | let alert = UIAlertController(title: title, 46 | message: message, 47 | preferredStyle: UI_USER_INTERFACE_IDIOM() == .pad ? .alert : .actionSheet) 48 | alert.addActions(actions) 49 | let alertActionCancel = UIAlertAction(title: cancelTitle, style: .cancel, handler: nil) 50 | alert.addAction(alertActionCancel) 51 | DispatchQueue.main.async(execute: { 52 | viewController.present(alert, animated: true, completion: nil) 53 | if tintColor != nil { 54 | alert.view.tintColor = tintColor! 55 | } 56 | }) 57 | } 58 | 59 | func addAction(title: String?, style: UIAlertActionStyle, handler: ((UIAlertAction) -> Void)?) { 60 | let action = UIAlertAction(title: title, style: style, handler: handler) 61 | addAction(action) 62 | } 63 | 64 | func addCancelAction(title: String?) { 65 | let action = UIAlertAction(title: title, style: .cancel, handler: nil) 66 | addAction(action) 67 | } 68 | } 69 | 70 | // MARK:- Add methods 71 | public extension UIAlertController { 72 | public func addActions(_ actions: [UIAlertAction]?) { 73 | actions?.forEach { self.addAction($0) } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Source/Common/Extensions/UICollectionView+Nib.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UICollectionView+Nib.swift 3 | // Worker Dashy 4 | // 5 | // Created by Maksim Kazachkov on 17.08.17. 6 | // Copyright © 2017 Umbrella. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UICollectionView { 12 | func registerNib(_ cellType: T.Type) { 13 | let className = String(describing: cellType) 14 | let nib = UINib(nibName: className, bundle: nil) 15 | let identifier = className + "ID" 16 | register(nib, forCellWithReuseIdentifier: identifier) 17 | } 18 | 19 | func registerNibForSupplementaryViewOfKind(_ cellType: T.Type, kind: String) { 20 | let className = String(describing: cellType) 21 | let nib = UINib(nibName: className, bundle: nil) 22 | let identifier = className + "ID" 23 | register(nib, forSupplementaryViewOfKind: kind, withReuseIdentifier: identifier) 24 | } 25 | 26 | func dequeueReusableCell(_ cellType: T.Type, indexPath: IndexPath) -> T { 27 | let className = String(describing: cellType) 28 | let identifier = className + "ID" 29 | return dequeueReusableCell(withReuseIdentifier: identifier, for: indexPath) as! T 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Source/Common/Extensions/UIViewController+Creation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIViewController+Creation.swift 3 | // Worker Dashy 4 | // 5 | // Created by Maksim Kazachkov on 16.08.17. 6 | // Copyright © 2017 Umbrella. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIViewController { 12 | static func controllerInStoryboard(_ storyboard: UIStoryboard, identifier: String) -> Self { 13 | return instantiateControllerInStoryboard(storyboard, identifier: identifier) 14 | } 15 | 16 | static func controllerInStoryboard(_ storyboard: UIStoryboard) -> Self { 17 | return controllerInStoryboard(storyboard, identifier: nameOfClass()) 18 | } 19 | 20 | static func controllerFromStoryboard(_ storyboard: Storyboards) -> Self { 21 | return controllerInStoryboard(UIStoryboard(name: storyboard.rawValue, bundle: nil), identifier: nameOfClass()) 22 | } 23 | } 24 | 25 | private extension UIViewController { 26 | static func instantiateControllerInStoryboard(_ storyboard: UIStoryboard, identifier: String) -> T { 27 | return storyboard.instantiateViewController(withIdentifier: identifier) as! T 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Source/Common/Factories/Coordinator/CoordinatorFactory.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CoordinatorFactory.swift 3 | // Worker Dashy 4 | // 5 | // Created by Maksim Kazachkov on 16.08.17. 6 | // Copyright © 2017 Umbrella. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | final class CoordinatorFactory { 12 | fileprivate let modulesFactory = ModulesFactory() 13 | } 14 | 15 | // MARK:- CoordinatorFactoryProtocol 16 | extension CoordinatorFactory: CoordinatorFactoryProtocol { 17 | func makeOnboardingCoordinator(router: Routable) -> Coordinatable & OnboardingCoordinatorOutput { 18 | return OnboardingCoordinator(with: modulesFactory, router: router) 19 | } 20 | 21 | func makeAuthorizationCoordinator(router: Routable) -> AuthorizationCoordinatorOutput & Coordinatable { 22 | return AuthorizationCoordinator(with: modulesFactory, router: router) 23 | } 24 | 25 | func makeMainCoordinator(router: Routable) -> Coordinatable & MainCoordinatorOutput { 26 | return MainCoordinator(with: modulesFactory, router: router) 27 | } 28 | } 29 | 30 | // MARK:- Private methods 31 | private extension CoordinatorFactory { 32 | func router(_ navController: UINavigationController?, in storyboard: Storyboards) -> Routable { 33 | return Router(rootController: navigationController(navController, in: storyboard)) 34 | } 35 | 36 | func navigationController(_ navController: UINavigationController?, in storyboard: Storyboards) -> UINavigationController { 37 | return navController == nil ? UINavigationController.controllerFromStoryboard(storyboard) : navController! 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Source/Common/Factories/Coordinator/CoordinatorFactoryProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CoordinatorFactoryProtocol.swift 3 | // Worker Dashy 4 | // 5 | // Created by Maksim Kazachkov on 16.08.17. 6 | // Copyright © 2017 Umbrella. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol CoordinatorFactoryProtocol { 12 | func makeOnboardingCoordinator(router: Routable) -> Coordinatable & OnboardingCoordinatorOutput 13 | func makeAuthorizationCoordinator(router: Routable) -> Coordinatable & AuthorizationCoordinatorOutput 14 | func makeMainCoordinator(router: Routable) -> Coordinatable & MainCoordinatorOutput 15 | } 16 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Source/Common/Factories/Modules/Authorization/AuthorizationFactoryProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AuthorizationFactory.swift 3 | // Worker Dashy 4 | // 5 | // Created by Maksim Kazachkov on 17.08.17. 6 | // Copyright © 2017 Umbrella. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol AuthorizationFactoryProtocol { 12 | func makeEnterView() -> EnterViewProtocol 13 | } 14 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Source/Common/Factories/Modules/Main/MainFactoryProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MainFactoryProtocol.swift 3 | // Worker Dashy 4 | // 5 | // Created by Maksim Kazachkov on 17.08.17. 6 | // Copyright © 2017 Umbrella. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol MainFactoryProtocol { 12 | func makeCharatersView() -> CharactersViewProtocol 13 | } 14 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Source/Common/Factories/Modules/ModulesFactory.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ModulesFactory.swift 3 | // Worker Dashy 4 | // 5 | // Created by Maksim Kazachkov on 17.08.17. 6 | // Copyright © 2017 Umbrella. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | final class ModulesFactory {} 12 | 13 | // MARK:- AuthorizationFactoryProtocol 14 | extension ModulesFactory: AuthorizationFactoryProtocol { 15 | func makeEnterView() -> EnterViewProtocol { 16 | let view: EnterViewController = EnterViewController.controllerFromStoryboard(.authorization) 17 | EnterAssembly.assembly(with: view) 18 | return view 19 | } 20 | } 21 | 22 | // MARK:- OnboardingFactoryProtocol 23 | extension ModulesFactory: OnboardingFactoryProtocol { 24 | func makeOnboardingView() -> OnboardingViewProtocol { 25 | let view: OnboardingPageViewController = OnboardingPageViewController.controllerFromStoryboard(.onboarding) 26 | return view 27 | } 28 | } 29 | 30 | // MARK:- MainFactoryProtocol 31 | extension ModulesFactory: MainFactoryProtocol { 32 | func makeCharatersView() -> CharactersViewProtocol { 33 | let view: CharactersViewController = CharactersViewController.controllerFromStoryboard(.main) 34 | CharactersAssembly.assembly(with: view) 35 | return view 36 | } 37 | } 38 | 39 | // MARK:- Private methods 40 | private extension ModulesFactory { 41 | func router(_ navController: UINavigationController?, in storyboard: Storyboards) -> Routable { 42 | return Router(rootController: navigationController(navController, in: storyboard)) 43 | } 44 | 45 | func navigationController(_ navController: UINavigationController?, in storyboard: Storyboards) -> UINavigationController { 46 | return navController == nil ? UINavigationController.controllerFromStoryboard(storyboard) : navController! 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Source/Common/Factories/Modules/Onboarding/OnboardingFactoryProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OnboardingFactoryProtocol.swift 3 | // CleanArchitectureWithCoordinatorPatternDemo 4 | // 5 | // Created by Maksim Kazachkov on 06.11.2017. 6 | // Copyright © 2017 Maksim Kazachkov. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol OnboardingFactoryProtocol { 12 | func makeOnboardingView() -> OnboardingViewProtocol 13 | } 14 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Source/Common/Helpers/Constants.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Constants.swift 3 | // Worker Dashy 4 | // 5 | // Created by Maksim Kazachkov on 16.08.17. 6 | // Copyright © 2017 Umbrella. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | // MARK:- Typealiases 12 | typealias CompletionBlock = () -> Void 13 | typealias AlertCompletionBlock = (String) -> Void 14 | 15 | // MARK:- Storyboards enum 16 | enum Storyboards: String { 17 | 18 | case authorization = "Auth" 19 | case main = "Main" 20 | case onboarding = "Onboarding" 21 | 22 | } 23 | 24 | // MARK:- PersistantKeys enum 25 | enum PersistantKeys { 26 | 27 | static let isSeenOnboarding = "kIsSeenOnboarding" 28 | static let token = "kToken" 29 | 30 | } 31 | 32 | enum Urls { 33 | static let base = "http://gateway.marvel.com" 34 | 35 | enum Endpoints { 36 | static let characters = "/v1/public/characters" 37 | } 38 | 39 | static let timestamp: NSNumber = 1 40 | } 41 | 42 | enum ApiKeys { 43 | static let publicKey = "44c6606f72114e4a9eaf84cb93fb8863" 44 | static let privateKey = "a92b6b3b8140846adf4eeebb64d576b5570be727" 45 | } 46 | 47 | // MARK:- Erroring 48 | enum ErrorCode: Int { 49 | case nilResponse = -11 50 | case emptyResponse = -10 51 | case undefined = -1 52 | } 53 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Source/Common/Helpers/RequestWrapper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GoogleMapsRequestWrapper.swift 3 | // Worker Dashy 4 | // 5 | // Created by Maksim Kazachkov on 23.08.17. 6 | // Copyright © 2017 Umbrella. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Alamofire 11 | import ObjectMapper 12 | 13 | public class RequestWrapper { 14 | 15 | let requestManager: RequestManager 16 | 17 | init(with configuration: RequestManagerConfiguration) { 18 | requestManager = RequestManager(configuration: configuration) 19 | } 20 | 21 | open func makeRequestWithoutMapping( 22 | URL: String, 23 | method: HTTPMethod = .post, 24 | encoding: ParameterEncoding? = nil, 25 | parameters: Parameters? = nil, 26 | headers: Headers? = nil, 27 | failure: @escaping Failure, 28 | success: @escaping Success) 29 | { 30 | requestManager.makeRequestWithoutMapping(URL: URL, 31 | method: method, 32 | encoding: encoding, 33 | parameters: parameters, 34 | headers: configureHeaders(headers), 35 | failure: failure, 36 | success: success) 37 | } 38 | 39 | open func makeRequestObject( 40 | URL: String, 41 | method: HTTPMethod = .post, 42 | encoding: ParameterEncoding? = nil, 43 | parameters: Parameters? = nil, 44 | headers: Headers? = nil, 45 | keyPath: String, 46 | failure: @escaping Failure, 47 | success: @escaping Success, 48 | successNoMapping: SuccessNoMapping? = nil, 49 | responseHeaders: ResponseHeaders? = nil) 50 | { 51 | requestManager.makeRequestObject(URL: URL, 52 | method: method, 53 | encoding: encoding, 54 | parameters: parameters, 55 | headers: configureHeaders(headers), 56 | keyPath: keyPath, 57 | failure: failure, 58 | success: success, 59 | successNoMapping: successNoMapping, 60 | successHeaders: responseHeaders) 61 | } 62 | 63 | open func makeRequest( 64 | URL: String, 65 | method: HTTPMethod = .post, 66 | encoding: ParameterEncoding? = nil, 67 | parameters: Parameters? = nil, 68 | headers: Headers? = nil, 69 | keyPath: String, 70 | failure: @escaping Failure, 71 | success: @escaping Success<[T]>, 72 | successNoMapping: SuccessNoMapping? = nil) 73 | { 74 | requestManager.makeRequest(URL: URL, 75 | method: method, 76 | encoding: encoding, 77 | parameters: parameters, 78 | headers: configureHeaders(headers), 79 | keyPath: keyPath, 80 | failure: failure, 81 | success: success) 82 | } 83 | } 84 | 85 | extension RequestWrapper { 86 | func configureHeaders(_ headers: Headers? = nil) -> Headers { 87 | var _headers: Headers = [:] 88 | if let headers = headers { 89 | _headers += headers 90 | } 91 | return _headers 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Source/Common/Helpers/Router.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Router.swift 3 | // Worker Dashy 4 | // 5 | // Created by Maksim Kazachkov on 16.08.17. 6 | // Copyright © 2017 Umbrella. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | typealias RouterCompletions = [UIViewController : CompletionBlock] 12 | 13 | final class Router: NSObject { 14 | 15 | // MARK:- Private variables 16 | fileprivate weak var rootController: UINavigationController? 17 | 18 | fileprivate var completions: RouterCompletions 19 | 20 | init(rootController: UINavigationController) { 21 | self.rootController = rootController 22 | completions = [:] 23 | } 24 | 25 | var toPresent: UIViewController? { 26 | return rootController 27 | } 28 | } 29 | 30 | // MARK:- Private methods 31 | private extension Router { 32 | func runCompletion(for controller: UIViewController) { 33 | guard let completion = completions[controller] else { return } 34 | completion() 35 | completions.removeValue(forKey: controller) 36 | } 37 | } 38 | 39 | // MARK:- Routable 40 | extension Router: Routable { 41 | func present(_ module: Presentable?) { 42 | present(module, animated: true) 43 | } 44 | 45 | func present(_ module: Presentable?, animated: Bool) { 46 | guard let controller = module?.toPresent else { return } 47 | rootController?.present(controller, animated: animated, completion: nil) 48 | } 49 | 50 | func push(_ module: Presentable?) { 51 | push(module, animated: true) 52 | } 53 | 54 | func push(_ module: Presentable?, animated: Bool) { 55 | push(module, animated: animated, completion: nil) 56 | } 57 | 58 | func push(_ module: Presentable?, animated: Bool, completion: CompletionBlock?) { 59 | guard 60 | let controller = module?.toPresent, 61 | !(controller is UINavigationController) 62 | else { assertionFailure("⚠️Deprecated push UINavigationController."); return } 63 | 64 | if let completion = completion { 65 | completions[controller] = completion 66 | } 67 | rootController?.pushViewController(controller, animated: animated) 68 | } 69 | 70 | func popModule() { 71 | popModule(animated: true) 72 | } 73 | 74 | func popModule(animated: Bool) { 75 | if let controller = rootController?.popViewController(animated: animated) { 76 | runCompletion(for: controller) 77 | } 78 | } 79 | 80 | func dismissModule() { 81 | dismissModule(animated: true, completion: nil) 82 | } 83 | 84 | func dismissModule(animated: Bool, completion: CompletionBlock?) { 85 | rootController?.dismiss(animated: animated, completion: completion) 86 | } 87 | 88 | func setRootModule(_ module: Presentable?) { 89 | setRootModule(module, hideBar: false) 90 | } 91 | 92 | func setRootModule(_ module: Presentable?, hideBar: Bool) { 93 | guard let controller = module?.toPresent else { return } 94 | rootController?.setViewControllers([controller], animated: false) 95 | rootController?.isNavigationBarHidden = hideBar 96 | } 97 | 98 | func popToRootModule(animated: Bool) { 99 | if let controllers = rootController?.popToRootViewController(animated: animated) { 100 | controllers.forEach { controller in 101 | runCompletion(for: controller) 102 | } 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Source/Common/Helpers/Session.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Session.swift 3 | // Worker Dashy 4 | // 5 | // Created by Maksim Kazachkov on 16.08.17. 6 | // Copyright © 2017 Umbrella. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | typealias Credentials = (username: String, password: String) 12 | 13 | struct Session { 14 | static var isAuthorized: Bool { 15 | return UserDefaultsWrapper.token != nil 16 | } 17 | 18 | static var isSeenOnboarding: Bool { 19 | get { 20 | return UserDefaultsWrapper.isSeenOnboarding 21 | } 22 | set { 23 | UserDefaultsWrapper.isSeenOnboarding = newValue 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Source/Common/Helpers/UserDefaultsWrapper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UserDefaultsWrapper.swift 3 | // CleanArchitectureWithCoordinatorPatternDemo 4 | // 5 | // Created by Maksim Kazachkov on 11.11.2017. 6 | // Copyright © 2017 Maksim Kazachkov. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct UserDefaultsWrapper { 12 | 13 | fileprivate static let UserDefaultsStandart = UserDefaults.standard 14 | 15 | static var isSeenOnboarding: Bool { 16 | get { 17 | return UserDefaultsStandart.bool(forKey: PersistantKeys.isSeenOnboarding) 18 | } 19 | set { 20 | UserDefaultsStandart.set(newValue, forKey: PersistantKeys.isSeenOnboarding) 21 | UserDefaultsStandart.synchronize() 22 | } 23 | } 24 | 25 | static var token: String? { 26 | get { 27 | return UserDefaultsStandart.string(forKey: PersistantKeys.token) 28 | } 29 | set { 30 | UserDefaultsStandart.set(newValue, forKey: PersistantKeys.token) 31 | UserDefaultsStandart.synchronize() 32 | } 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Source/Common/Managers/Networking/NSError+Creation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSError+Creation.swift 3 | // Worker Dashy 4 | // 5 | // Created by Maksim Kazachkov on 23.08.17. 6 | // Copyright © 2017 Umbrella. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension NSError { 12 | 13 | public static func error(title: String, code: Int) -> NSError { 14 | let bundleName = Bundle.main.bundleIdentifier! 15 | return NSError(domain: bundleName, code: code, userInfo: [NSLocalizedDescriptionKey : title]) 16 | } 17 | 18 | public static func error(title: String) -> NSError { 19 | return error(title: title, code: 0) 20 | } 21 | } 22 | 23 | extension NSError { 24 | static func error(title: String, code: ErrorCode) -> NSError { 25 | return NSError.error(title: title, code: code.rawValue) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Source/Common/Managers/Networking/RequestManagerConfiguration.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RequestManagerConfiguration.swift 3 | // Worker Dashy 4 | // 5 | // Created by Maksim Kazachkov on 23.08.17. 6 | // Copyright © 2017 Umbrella. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import ObjectMapper 11 | import Alamofire 12 | 13 | public typealias ErrorMapping = (Any) -> (RequestManagerErroring?) 14 | public typealias HttpErrorHandler = (Int?) -> Void 15 | 16 | public struct RequestManagerConfiguration { 17 | public var baseURL: String 18 | 19 | public var defaultParams : Parameters? 20 | public var defaultHeaders : Headers? 21 | public var timeout : TimeInterval = 10 22 | public var logging : Bool = false 23 | public var errorMap : ErrorMapping? 24 | public var undefinedError : NSError 25 | public var nilResponseError : NSError 26 | public var encoding : ParameterEncoding 27 | public var httpErrorHandler : HttpErrorHandler? 28 | 29 | public init(baseURL : String, 30 | params : Parameters? = nil, 31 | headers : Headers? = nil, 32 | timeout : TimeInterval = 10, 33 | encoding : ParameterEncoding = URLEncoding.default, 34 | logging : Bool = false, 35 | nilResponseError : NSError = .error(title : "Check your internet connection", code : -11), 36 | undefinedError : NSError = .error(title : "Sorry, but something went wrong", code : -1), 37 | httpErrorHanlder : HttpErrorHandler? = nil, 38 | errorMap : ErrorMapping? = nil) 39 | { 40 | self.baseURL = baseURL 41 | self.defaultParams = params 42 | self.defaultHeaders = headers 43 | self.timeout = timeout 44 | self.encoding = encoding 45 | self.logging = logging 46 | self.undefinedError = undefinedError 47 | self.nilResponseError = nilResponseError 48 | self.errorMap = errorMap 49 | self.httpErrorHandler = httpErrorHanlder 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Source/Common/Managers/Networking/RequestManagerErroring.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RequestManagerErroring.swift 3 | // Worker Dashy 4 | // 5 | // Created by Maksim Kazachkov on 23.08.17. 6 | // Copyright © 2017 Umbrella. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import ObjectMapper 11 | 12 | public protocol RequestManagerErrorMapping: Mappable { 13 | var code : Int? { get set } 14 | var message : String? { get set } 15 | } 16 | 17 | public protocol RequestManagerErroring { 18 | var description: String? { get } 19 | var errorCode: Int { get } 20 | func defaultError() -> Self 21 | func description(code: Int) -> String? 22 | } 23 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Source/Common/Model/Character.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Character.swift 3 | // Marvel 4 | // 5 | // Created by Максим on 06.09.16. 6 | // Copyright © 2016 Максим. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import ObjectMapper 11 | 12 | struct Character { 13 | 14 | var uid : Int? 15 | var name = "" 16 | var desc = "" 17 | var modified = "" 18 | var resourceURI = "" 19 | var comics : Comics? 20 | var events : Events? 21 | var image : Thumbnail? 22 | var series : Series? 23 | var stories : Stories? 24 | var urls : [URLS]? 25 | } 26 | 27 | // MARK:- 28 | extension Character: Mappable { 29 | 30 | mutating func mapping(map: Map) { 31 | uid <- map["id"] 32 | name <- map["name"] 33 | desc <- map["description"] 34 | modified <- map["modified"] 35 | resourceURI <- map["resourceURI"] 36 | comics <- map["comics"] 37 | events <- map["events"] 38 | image <- map["thumbnail"] 39 | series <- map["series"] 40 | stories <- map["stories"] 41 | urls <- map["urls"] 42 | } 43 | 44 | init?(map: Map) {} 45 | 46 | } 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Source/Common/Model/Comics.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Comics.swift 3 | // Marvel 4 | // 5 | // Created by Максим on 06.09.16. 6 | // Copyright © 2016 Максим. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import ObjectMapper 11 | 12 | struct Comics { 13 | 14 | var available : Int? 15 | var collectionURI = "" 16 | var comicsItems : [ComicsItem]? 17 | } 18 | 19 | // MARK:- BaseMappable 20 | extension Comics: Mappable { 21 | 22 | mutating func mapping(map: Map) { 23 | available <- map["available"] 24 | collectionURI <- map["collectionURI"] 25 | comicsItems <- map["items"] 26 | } 27 | 28 | init?(map: Map) {} 29 | 30 | } 31 | 32 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Source/Common/Model/ComicsItem.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ComicsItem.swift 3 | // Marvel 4 | // 5 | // Created by Максим on 06.09.16. 6 | // Copyright © 2016 Максим. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import ObjectMapper 11 | 12 | struct ComicsItem { 13 | 14 | var name = "" 15 | var resourceURI = "" 16 | 17 | } 18 | 19 | extension ComicsItem: Mappable { 20 | 21 | mutating func mapping(map: Map) { 22 | name <- map["name"] 23 | resourceURI <- map["resourceURI"] 24 | } 25 | 26 | init?(map: Map) {} 27 | 28 | } 29 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Source/Common/Model/Events.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Events.swift 3 | // Marvel 4 | // 5 | // Created by Максим on 06.09.16. 6 | // Copyright © 2016 Максим. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import ObjectMapper 11 | 12 | struct Events { 13 | 14 | var available : Int? 15 | var collectionURI = "" 16 | var eventsItems : [EventsItem]? 17 | 18 | } 19 | 20 | // MARK:- BaseMappable 21 | extension Events: Mappable { 22 | 23 | mutating func mapping(map: Map) { 24 | available <- map["available"] 25 | collectionURI <- map["collectionURI"] 26 | eventsItems <- map["items"] 27 | } 28 | 29 | init?(map: Map) {} 30 | 31 | } 32 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Source/Common/Model/EventsItem.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EventsItem.swift 3 | // Marvel 4 | // 5 | // Created by Максим on 06.09.16. 6 | // Copyright © 2016 Максим. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import ObjectMapper 11 | 12 | struct EventsItem { 13 | 14 | var name = "" 15 | var resourceURI = "" 16 | } 17 | 18 | // MARK:- BaseMappable 19 | extension EventsItem: Mappable { 20 | 21 | mutating func mapping(map: Map) { 22 | name <- map["name"] 23 | resourceURI <- map["resourceURI"] 24 | } 25 | 26 | init?(map: Map) {} 27 | 28 | } 29 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Source/Common/Model/Series.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Series.swift 3 | // Marvel 4 | // 5 | // Created by Максим on 06.09.16. 6 | // Copyright © 2016 Максим. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import ObjectMapper 11 | 12 | struct Series { 13 | 14 | var available : Int? 15 | var collectionURI = "" 16 | var seriesItems : [SeriesItem]? 17 | 18 | } 19 | 20 | // MARK:- BaseMappable 21 | extension Series: Mappable { 22 | 23 | mutating func mapping(map: Map) { 24 | available <- map["available"] 25 | collectionURI <- map["collectionURI"] 26 | seriesItems <- map["items"] 27 | } 28 | 29 | init?(map: Map) {} 30 | 31 | } 32 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Source/Common/Model/SeriesItem.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SeriesItem.swift 3 | // Marvel 4 | // 5 | // Created by Максим on 06.09.16. 6 | // Copyright © 2016 Максим. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import ObjectMapper 11 | 12 | struct SeriesItem { 13 | 14 | var name = "" 15 | var resourceURI = "" 16 | 17 | } 18 | 19 | // MARK:- BaseMappable 20 | extension SeriesItem: Mappable { 21 | 22 | mutating func mapping(map: Map) { 23 | name <- map["name"] 24 | resourceURI <- map["resourceURI"] 25 | } 26 | 27 | init?(map: Map) {} 28 | 29 | } 30 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Source/Common/Model/Stories.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Stories.swift 3 | // Marvel 4 | // 5 | // Created by Максим on 06.09.16. 6 | // Copyright © 2016 Максим. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import ObjectMapper 11 | 12 | struct Stories { 13 | 14 | var available : Int? 15 | var collectionURI = "" 16 | var storiesItems : [StoriesItem]? 17 | } 18 | 19 | // MARK:- BaseMappable 20 | extension Stories: Mappable { 21 | 22 | mutating func mapping(map: Map) { 23 | available <- map["available"] 24 | collectionURI <- map["collectionURI"] 25 | storiesItems <- map["items"] 26 | } 27 | 28 | init?(map: Map) {} 29 | 30 | } 31 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Source/Common/Model/StoriesItem.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StoriesIrem.swift 3 | // Marvel 4 | // 5 | // Created by Максим on 06.09.16. 6 | // Copyright © 2016 Максим. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import ObjectMapper 11 | 12 | struct StoriesItem { 13 | 14 | var name = "" 15 | var resourceURI = "" 16 | var type = "" 17 | 18 | } 19 | 20 | // MARK:- BaseMappable 21 | extension StoriesItem: Mappable { 22 | 23 | mutating func mapping(map: Map) { 24 | name <- map["name"] 25 | resourceURI <- map["resourceURI"] 26 | type <- map["type"] 27 | } 28 | 29 | init?(map: Map) {} 30 | 31 | } 32 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Source/Common/Model/Thumbnail.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Thumbnail.swift 3 | // Marvel 4 | // 5 | // Created by Максим on 06.09.16. 6 | // Copyright © 2016 Максим. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import ObjectMapper 11 | 12 | struct Thumbnail { 13 | 14 | var path = "" 15 | var ext = "" 16 | 17 | } 18 | 19 | // MARK:- BaseMappable 20 | extension Thumbnail: Mappable { 21 | 22 | mutating func mapping(map: Map) { 23 | path <- map["path"] 24 | ext <- map["extension"] 25 | } 26 | 27 | init?(map: Map) {} 28 | 29 | } 30 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Source/Common/Model/URLS.swift: -------------------------------------------------------------------------------- 1 | // 2 | // URLS.swift 3 | // Marvel 4 | // 5 | // Created by Максим on 06.09.16. 6 | // Copyright © 2016 Максим. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import ObjectMapper 11 | 12 | struct URLS { 13 | 14 | var type = "" 15 | var url = "" 16 | } 17 | 18 | // MARK:- BaseMappable 19 | extension URLS: Mappable { 20 | 21 | mutating func mapping(map: Map) { 22 | type <- map["type"] 23 | url <- map["url"] 24 | } 25 | 26 | init?(map: Map) {} 27 | 28 | } 29 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Source/Common/Protocols/BaseViewProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BaseViewProtocol.swift 3 | // Worker Dashy 4 | // 5 | // Created by Maksim Kazachkov on 17.08.17. 6 | // Copyright © 2017 Umbrella. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol BaseViewProtocol: NSObjectProtocol, Presentable {} 12 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Source/Common/Protocols/Coordinatable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Coordinatable.swift 3 | // Worker Dashy 4 | // 5 | // Created by Maksim Kazachkov on 16.08.17. 6 | // Copyright © 2017 Umbrella. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol Coordinatable: class { 12 | func start() 13 | } 14 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Source/Common/Protocols/Presentable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Presentable.swift 3 | // Worker Dashy 4 | // 5 | // Created by Maksim Kazachkov on 16.08.17. 6 | // Copyright © 2017 Umbrella. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol Presentable { 12 | var toPresent: UIViewController? { get } 13 | } 14 | 15 | extension UIViewController: Presentable { 16 | var toPresent: UIViewController? { 17 | return self 18 | } 19 | 20 | func showAlert(title: String, message: String? = nil) { 21 | UIAlertController.showAlert(title : title, 22 | message : message, 23 | inViewController : self, 24 | actionBlock : nil) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Source/Common/Protocols/Routable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Routable.swift 3 | // Worker Dashy 4 | // 5 | // Created by Maksim Kazachkov on 16.08.17. 6 | // Copyright © 2017 Umbrella. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol Routable: Presentable { 12 | 13 | func present(_ module: Presentable?) 14 | func present(_ module: Presentable?, animated: Bool) 15 | 16 | func push(_ module: Presentable?) 17 | func push(_ module: Presentable?, animated: Bool) 18 | func push(_ module: Presentable?, animated: Bool, completion: CompletionBlock?) 19 | 20 | func popModule() 21 | func popModule(animated: Bool) 22 | 23 | func dismissModule() 24 | func dismissModule(animated: Bool, completion: CompletionBlock?) 25 | 26 | func setRootModule(_ module: Presentable?) 27 | func setRootModule(_ module: Presentable?, hideBar: Bool) 28 | 29 | func popToRootModule(animated: Bool) 30 | } 31 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Source/Modules/Flows/Authorization/AuthorizationCoordinator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AuthorizationCoordinator.swift 3 | // Worker Dashy 4 | // 5 | // Created by Maksim Kazachkov on 17.08.17. 6 | // Copyright © 2017 Umbrella. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | final class AuthorizationCoordinator: BaseCoordinator, AuthorizationCoordinatorOutput { 12 | 13 | var finishFlow: CompletionBlock? 14 | 15 | fileprivate let factory: AuthorizationFactoryProtocol 16 | fileprivate let router : Routable 17 | 18 | init(with factory: AuthorizationFactoryProtocol, router: Routable) { 19 | self.factory = factory 20 | self.router = router 21 | } 22 | } 23 | 24 | // MARK:- Coordinatable 25 | extension AuthorizationCoordinator: Coordinatable { 26 | func start() { 27 | performFlow() 28 | } 29 | } 30 | 31 | // MARK:- Private methods 32 | private extension AuthorizationCoordinator { 33 | func performFlow() { 34 | let view = factory.makeEnterView() 35 | view.onCompletion = finishFlow 36 | router.setRootModule(view, hideBar: true) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Source/Modules/Flows/Authorization/AuthorizationCoordinatorOutput.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AuthorizationCoordinatorOutput.swift 3 | // Worker Dashy 4 | // 5 | // Created by Maksim Kazachkov on 17.08.17. 6 | // Copyright © 2017 Umbrella. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol AuthorizationCoordinatorOutput: class { 12 | var finishFlow: CompletionBlock? { get set } 13 | } 14 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Source/Modules/Flows/Authorization/Controllers/Enter/EnterAssembly.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EnterAssembly.swift 3 | // CleanArchitectureWithCoordinatorPatternDemo 4 | // 5 | // Created Maksim Kazachkov on 10.11.2017. 6 | // Copyright © 2017 Maksim Kazachkov. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol EnterAssemblable: EnterViewProtocol, EnterPresenterOutput {} 12 | 13 | final class EnterAssembly { 14 | static func assembly(with output: EnterPresenterOutput) { 15 | let interactor = EnterPresenter() 16 | let presenter = EnterInteractor() 17 | 18 | interactor.presenter = presenter 19 | presenter.interactor = interactor 20 | presenter.output = output 21 | output.presenter = presenter 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Source/Modules/Flows/Authorization/Controllers/Enter/EnterInteractor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EnterInteractor.swift 3 | // CleanArchitectureWithCoordinatorPatternDemo 4 | // 5 | // Created Maksim Kazachkov on 10.11.2017. 6 | // Copyright © 2017 Maksim Kazachkov. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | final class EnterInteractor { 12 | 13 | weak var presenter: EnterPresenter? 14 | } 15 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Source/Modules/Flows/Authorization/Controllers/Enter/EnterPresenter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EnterPresenter.swift 3 | // CleanArchitectureWithCoordinatorPatternDemo 4 | // 5 | // Created Maksim Kazachkov on 10.11.2017. 6 | // Copyright © 2017 Maksim Kazachkov. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol EnterPresenterInput: class { 12 | 13 | } 14 | 15 | protocol EnterPresenterOutput: class { 16 | var interactor: EnterPresenterInput? { get set } 17 | } 18 | 19 | final class EnterPresenter { 20 | 21 | weak var output: EnterPresenterOutput? 22 | 23 | var interactor: EnterInteractor? 24 | } 25 | 26 | extension EnterPresenter: EnterPresenterInput { 27 | 28 | } 29 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Source/Modules/Flows/Authorization/Controllers/Enter/EnterViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EnterViewController.swift 3 | // CleanArchitectureWithCoordinatorPatternDemo 4 | // 5 | // Created Maksim Kazachkov on 10.11.2017. 6 | // Copyright © 2017 Maksim Kazachkov. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class EnterViewController: UIViewController, EnterAssemblable { 12 | 13 | var presenter: EnterPresenter? 14 | 15 | override func viewDidLoad() { 16 | super.viewDidLoad() 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Source/Modules/Flows/Authorization/Controllers/EnterAssembly.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EnterAssembly.swift 3 | // CleanArchitectureWithCoordinatorPatternDemo 4 | // 5 | // Created Maksim Kazachkov on 11.11.2017. 6 | // Copyright © 2017 Maksim Kazachkov. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol EnterAssemblable: EnterViewProtocol, EnterPresenterOutput {} 12 | 13 | final class EnterAssembly { 14 | static func assembly(with output: EnterPresenterOutput) { 15 | let interactor = EnterInteractor() 16 | let presenter = EnterPresenter() 17 | 18 | interactor.presenter = presenter 19 | presenter.interactor = interactor 20 | presenter.output = output 21 | output.presenter = presenter 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Source/Modules/Flows/Authorization/Controllers/EnterInteractor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EnterInteractor.swift 3 | // CleanArchitectureWithCoordinatorPatternDemo 4 | // 5 | // Created Maksim Kazachkov on 11.11.2017. 6 | // Copyright © 2017 Maksim Kazachkov. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | final class EnterInteractor { 12 | 13 | weak var presenter: EnterPresenter? 14 | 15 | func signIn() { 16 | UserDefaultsWrapper.token = "Some token" 17 | presenter?.handleSuccessSignIn() 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Source/Modules/Flows/Authorization/Controllers/EnterPresenter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EnterPresenter.swift 3 | // CleanArchitectureWithCoordinatorPatternDemo 4 | // 5 | // Created Maksim Kazachkov on 11.11.2017. 6 | // Copyright © 2017 Maksim Kazachkov. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol EnterPresenterInput: class { 12 | func onSignIn() 13 | } 14 | 15 | protocol EnterPresenterOutput: class { 16 | var presenter: EnterPresenterInput? { get set } 17 | 18 | func signInProceed() 19 | } 20 | 21 | final class EnterPresenter { 22 | 23 | weak var output: EnterPresenterOutput? 24 | 25 | var interactor: EnterInteractor? 26 | 27 | func handleSuccessSignIn() { 28 | output?.signInProceed() 29 | } 30 | } 31 | 32 | // MARK:- EnterPresenterInput 33 | extension EnterPresenter: EnterPresenterInput { 34 | func onSignIn() { 35 | interactor?.signIn() 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Source/Modules/Flows/Authorization/Controllers/EnterViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EnterViewController.swift 3 | // CleanArchitectureWithCoordinatorPatternDemo 4 | // 5 | // Created Maksim Kazachkov on 11.11.2017. 6 | // Copyright © 2017 Maksim Kazachkov. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class EnterViewController: UIViewController, EnterAssemblable { 12 | 13 | var presenter: EnterPresenterInput? 14 | 15 | var onCompletion: CompletionBlock? 16 | 17 | override func viewDidLoad() { 18 | super.viewDidLoad() 19 | } 20 | 21 | @IBAction func signInPressed(_ sender: UIButton) { 22 | presenter?.onSignIn() 23 | } 24 | } 25 | 26 | // MARK:- EnterPresenterOutput 27 | extension EnterViewController { 28 | func signInProceed() { 29 | onCompletion?() 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Source/Modules/Flows/Authorization/Controllers/EnterViewProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EnterViewProtocol.swift 3 | // CleanArchitectureWithCoordinatorPatternDemo 4 | // 5 | // Created Maksim Kazachkov on 11.11.2017. 6 | // Copyright © 2017 Maksim Kazachkov. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol EnterViewProtocol: BaseViewProtocol { 12 | var onCompletion: CompletionBlock? { get set } 13 | } 14 | 15 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Source/Modules/Flows/Main/Controllers/Cells/CharacterCollectionViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CharacterCVCC.swift 3 | // Marvel 4 | // 5 | // Created by Максим on 08.09.16. 6 | // Copyright © 2016 Максим. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Kingfisher 11 | 12 | class CharacterCollectionViewCell: UICollectionViewCell { 13 | 14 | var character : Character? { 15 | didSet { 16 | guard let character = character else { return } 17 | updateUI(with: character) 18 | } 19 | } 20 | 21 | fileprivate var imageView = UIImageView() 22 | 23 | // MARK: - Private 24 | @IBOutlet fileprivate weak var characterImageView: UIImageView! 25 | 26 | @IBOutlet fileprivate weak var characterNameLabel: UILabel! 27 | 28 | @IBOutlet fileprivate weak var textView: UIView! 29 | 30 | override func layoutSubviews() { 31 | super.layoutSubviews() 32 | layer.cornerRadius = 60.0 33 | clipsToBounds = true 34 | } 35 | } 36 | 37 | // MARK:- Private methods 38 | private extension CharacterCollectionViewCell { 39 | func updateUI(with character: Character) { 40 | characterNameLabel.text = character.name 41 | characterNameLabel.alpha = 0.0 42 | guard 43 | let imageURL = character.image?.path, 44 | let imageExtension = character.image?.ext, 45 | let resource = URL(string: imageURL + "/portrait_uncanny." + imageExtension) 46 | else { return } 47 | characterImageView.kf.setImage(with: resource, 48 | placeholder: nil, 49 | options: nil, 50 | progressBlock: nil) { [weak self] (image, _, _, _) in 51 | self?.characterNameLabel.alpha = 1.0 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Source/Modules/Flows/Main/Controllers/Characters/CharactersAssembly.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CharactersAssembly.swift 3 | // CleanArchitectureWithCoordinatorPatternDemo 4 | // 5 | // Created Maksim Kazachkov on 11.11.2017. 6 | // Copyright © 2017 Maksim Kazachkov. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol CharactersAssemblable: CharactersViewProtocol, CharactersPresenterOutput {} 12 | 13 | final class CharactersAssembly { 14 | static func assembly(with output: CharactersPresenterOutput) { 15 | let interactor = CharactersInteractor() 16 | let presenter = CharactersPresenter() 17 | 18 | interactor.presenter = presenter 19 | presenter.interactor = interactor 20 | presenter.output = output 21 | output.presenter = presenter 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Source/Modules/Flows/Main/Controllers/Characters/CharactersDataSource.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CharactersDataSource.swift 3 | // CleanArchitectureWithCoordinatorPatternDemo 4 | // 5 | // Created by Maksim Kazachkov on 11.11.2017. 6 | // Copyright © 2017 Maksim Kazachkov. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol CharactersDataSourceDelegate: class { 12 | var collectionView: UICollectionView! { get } 13 | var index: Int! { get set } 14 | } 15 | 16 | final class CharactersDataSource: NSObject { 17 | 18 | lazy var characters: [Character] = [] 19 | 20 | weak var delegate: CharactersDataSourceDelegate? 21 | } 22 | 23 | // MARK:- UICollectionViewDataSource 24 | extension CharactersDataSource: UICollectionViewDataSource { 25 | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 26 | return characters.count 27 | } 28 | 29 | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 30 | let cell = collectionView.dequeueReusableCell(CharacterCollectionViewCell.self, indexPath: indexPath) 31 | cell.character = characters[indexPath.item] 32 | return cell 33 | } 34 | } 35 | 36 | // MARK:- UIScrollViewDelegate 37 | extension CharactersDataSource : UIScrollViewDelegate { 38 | 39 | func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer) { 40 | 41 | let layout = delegate?.collectionView?.collectionViewLayout as! UICollectionViewFlowLayout 42 | 43 | let cellWidthIncludingSpacing = layout.itemSize.width + layout.minimumLineSpacing 44 | 45 | var offset = targetContentOffset.pointee 46 | 47 | let index = (offset.x + scrollView.contentInset.left) / cellWidthIncludingSpacing 48 | 49 | let roundedIndex = round(index) 50 | 51 | offset = CGPoint(x: roundedIndex * cellWidthIncludingSpacing - scrollView.contentInset.left, y: -scrollView.contentInset.top) 52 | targetContentOffset.pointee = offset 53 | } 54 | 55 | 56 | func scrollViewDidScroll(_ scrollView: UIScrollView) { 57 | 58 | guard let delegate = delegate else { return } 59 | 60 | let layout = delegate.collectionView?.collectionViewLayout as! UICollectionViewFlowLayout 61 | 62 | let cellWidthIncludingSpacing = layout.itemSize.width + layout.minimumLineSpacing 63 | 64 | let offset = scrollView.contentOffset 65 | 66 | let index = (offset.x + scrollView.contentInset.left) / cellWidthIncludingSpacing 67 | 68 | let roundedIndex = round(index) 69 | if Int(roundedIndex) > delegate.index || Int(roundedIndex) < delegate.index { 70 | delegate.index = Int(roundedIndex) 71 | } 72 | } 73 | } 74 | 75 | // MARK:- UICollectionViewDelegate 76 | extension CharactersDataSource: UICollectionViewDelegate {} 77 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Source/Modules/Flows/Main/Controllers/Characters/CharactersInteractor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CharactersInteractor.swift 3 | // CleanArchitectureWithCoordinatorPatternDemo 4 | // 5 | // Created Maksim Kazachkov on 11.11.2017. 6 | // Copyright © 2017 Maksim Kazachkov. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | final class CharactersInteractor { 12 | 13 | weak var presenter: CharactersPresenter? 14 | 15 | fileprivate let mainReqeustManager = MainRequestManager() 16 | 17 | func getCharacters(by offset: Int, count: Int) { 18 | mainReqeustManager.getCharacters(offset: offset, 19 | count: count, 20 | failure: { (_) in 21 | // TODO:- show alert? 22 | }) { [weak self] (result: [Character]) in 23 | guard let `self` = self else { return } 24 | self.presenter?.received(characters: result) 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Source/Modules/Flows/Main/Controllers/Characters/CharactersPresenter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CharactersPresenter.swift 3 | // CleanArchitectureWithCoordinatorPatternDemo 4 | // 5 | // Created Maksim Kazachkov on 11.11.2017. 6 | // Copyright © 2017 Maksim Kazachkov. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol CharactersPresenterInput: class { 12 | func getCharacters(by offset: Int, count: Int) 13 | } 14 | 15 | protocol CharactersPresenterOutput: class { 16 | var presenter: CharactersPresenterInput? { get set } 17 | 18 | func showActivity() 19 | func hideActivity() 20 | 21 | func received(characters: [Character]) 22 | } 23 | 24 | final class CharactersPresenter { 25 | 26 | weak var output: CharactersPresenterOutput? 27 | 28 | var interactor: CharactersInteractor? 29 | 30 | func received(characters: [Character]) { 31 | output?.hideActivity() 32 | output?.received(characters: characters) 33 | } 34 | } 35 | 36 | // MARK:- CharactersPresenterInput 37 | extension CharactersPresenter: CharactersPresenterInput { 38 | func getCharacters(by offset: Int, count: Int) { 39 | output?.showActivity() 40 | interactor?.getCharacters(by: offset, count: count) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Source/Modules/Flows/Main/Controllers/Characters/CharactersViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CharactersViewController.swift 3 | // CleanArchitectureWithCoordinatorPatternDemo 4 | // 5 | // Created Maksim Kazachkov on 11.11.2017. 6 | // Copyright © 2017 Maksim Kazachkov. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | final class CharactersViewController: UIViewController, CharactersAssemblable, CharactersDataSourceDelegate { 12 | 13 | var presenter: CharactersPresenterInput? 14 | 15 | @IBOutlet fileprivate weak var backgroundImageView : UIImageView! 16 | 17 | @IBOutlet fileprivate weak var loadCharactersIndicatorView : UIActivityIndicatorView! 18 | @IBOutlet fileprivate weak var activityIndicatorView : UIActivityIndicatorView! 19 | 20 | @IBOutlet weak var collectionView : UICollectionView! 21 | 22 | fileprivate var dataSource = CharactersDataSource() 23 | 24 | override func viewDidLoad() { 25 | super.viewDidLoad() 26 | setup() 27 | activityIndicatorView.startAnimating() 28 | presenter?.getCharacters(by: offset, count: count) 29 | } 30 | 31 | fileprivate let count: Int = 3 32 | 33 | fileprivate var offset : Int = 0 { 34 | didSet { 35 | presenter?.getCharacters(by: self.offset, count: self.count) 36 | } 37 | } 38 | 39 | var index : Int! = 0 { 40 | didSet { 41 | if index >= dataSource.characters.count - 3 { 42 | offset += 3 43 | } 44 | } 45 | } 46 | } 47 | 48 | // MARK:- Private methods 49 | private extension CharactersViewController { 50 | func setup() { 51 | setupCollectionView() 52 | } 53 | 54 | func setupCollectionView() { 55 | collectionView.dataSource = dataSource 56 | collectionView.delegate = dataSource 57 | 58 | dataSource.delegate = self 59 | } 60 | } 61 | 62 | // MARK:- CharactersPresenterOutput 63 | extension CharactersViewController { 64 | func showActivity() { 65 | dataSource.characters.isEmpty ? activityIndicatorView.startAnimating() : loadCharactersIndicatorView.startAnimating() 66 | } 67 | 68 | func hideActivity() { 69 | dataSource.characters.isEmpty ? activityIndicatorView.stopAnimating() : loadCharactersIndicatorView.stopAnimating() 70 | } 71 | 72 | func received(characters: [Character]) { 73 | dataSource.characters.append(contentsOf: characters) 74 | let newIndexPaths = IndexPath.configure(for: characters, 75 | with: dataSource.characters, 76 | section: 0) 77 | collectionView.insertItems(at: newIndexPaths) 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Source/Modules/Flows/Main/Controllers/Characters/CharactersViewProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CharactersViewProtocol.swift 3 | // CleanArchitectureWithCoordinatorPatternDemo 4 | // 5 | // Created Maksim Kazachkov on 11.11.2017. 6 | // Copyright © 2017 Maksim Kazachkov. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol CharactersViewProtocol: BaseViewProtocol { 12 | 13 | } 14 | 15 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Source/Modules/Flows/Main/MainCoordinator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MainCoordinator.swift 3 | // CleanArchitectureWithCoordinatorPatternDemo 4 | // 5 | // Created by Maksim Kazachkov on 11.11.2017. 6 | // Copyright © 2017 Maksim Kazachkov. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | final class MainCoordinator: BaseCoordinator, MainCoordinatorOutput { 12 | 13 | var finishFlow: CompletionBlock? 14 | 15 | fileprivate let factory: MainFactoryProtocol 16 | fileprivate let router : Routable 17 | 18 | init(with factory: MainFactoryProtocol, router: Routable) { 19 | self.factory = factory 20 | self.router = router 21 | } 22 | } 23 | 24 | // MARK:- Coordinatable 25 | extension MainCoordinator: Coordinatable { 26 | func start() { 27 | performFlow() 28 | } 29 | } 30 | 31 | // MARK:- Private methods 32 | private extension MainCoordinator { 33 | func performFlow() { 34 | let view = factory.makeCharatersView() 35 | router.setRootModule(view, hideBar: true) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Source/Modules/Flows/Main/MainCoordinatorOutput.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MainCoordinatorOutput.swift 3 | // CleanArchitectureWithCoordinatorPatternDemo 4 | // 5 | // Created by Maksim Kazachkov on 11.11.2017. 6 | // Copyright © 2017 Maksim Kazachkov. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol MainCoordinatorOutput: class { 12 | var finishFlow: CompletionBlock? { get set } 13 | } 14 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Source/Modules/Flows/Main/MainRequestManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MainRequestManager.swift 3 | // CleanArchitectureWithCoordinatorPatternDemo 4 | // 5 | // Created by Maksim Kazachkov on 11.11.2017. 6 | // Copyright © 2017 Maksim Kazachkov. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | final class MainRequestManager { 12 | 13 | let mainRequestManager: RequestWrapper 14 | 15 | let configuration = RequestManagerConfiguration(baseURL : Urls.base, 16 | params : ["ts" : Urls.timestamp, 17 | "apikey" : ApiKeys.publicKey, 18 | "hash" : MainRequestManager.hash], 19 | timeout : 20, 20 | logging : true) 21 | 22 | fileprivate static var hash: String! { 23 | return makeHash(Urls.timestamp, publicKey: ApiKeys.publicKey, privateKey: ApiKeys.privateKey) 24 | } 25 | 26 | fileprivate static func makeHash(_ timestamp : NSNumber, publicKey : String, privateKey : String) -> String { 27 | return "\(timestamp)\(privateKey)\(publicKey)".md5() 28 | } 29 | 30 | init() { 31 | mainRequestManager = RequestWrapper(with: configuration) 32 | } 33 | 34 | func getCharacters(offset : Int, 35 | count : Int, 36 | failure : @escaping Failure, 37 | success : @escaping Success<[Character]>) { 38 | mainRequestManager.makeRequest(URL : Urls.Endpoints.characters, 39 | method : .get, 40 | keyPath : "data.results", 41 | failure : failure, 42 | success : success) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Source/Modules/Flows/Onboarding/Controllers/OnboardingPageViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OnboardingPageViewController.swift 3 | // CleanArchitectureWithCoordinatorPatternDemo 4 | // 5 | // Created by Maksim Kazachkov on 10.11.2017. 6 | // Copyright © 2017 Maksim Kazachkov. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | final class OnboardingPageViewController: UIPageViewController, OnboardingViewProtocol { 12 | 13 | var onInfo : CompletionBlock? 14 | var onCompletion : CompletionBlock? 15 | 16 | // MARK:- Private variables 17 | fileprivate var numberOfViewControllers: Int { 18 | return images.count 19 | } 20 | 21 | fileprivate let images: [UIImage] = [#imageLiteral(resourceName: "art1"), 22 | #imageLiteral(resourceName: "art2"), 23 | #imageLiteral(resourceName: "art3")] 24 | 25 | // MARK:- UIViewController methods 26 | override func viewDidLoad() { 27 | super.viewDidLoad() 28 | setup() 29 | } 30 | } 31 | 32 | private extension OnboardingPageViewController { 33 | func setup() { 34 | dataSource = self 35 | 36 | if let viewController = showViewController(at: 0) { 37 | set(viewControllers: [viewController]) 38 | } 39 | } 40 | 41 | func showViewController(at index: Int) -> OnboardingViewController? { 42 | guard index != NSNotFound, index >= 0, index < numberOfViewControllers else { return nil } 43 | let viewController = OnboardingViewController.controllerFromStoryboard(.onboarding) 44 | viewController.index = index 45 | viewController.image = images[index] 46 | viewController.isLast = index == 2 47 | viewController.onGoMarvel = onCompletion 48 | viewController.onInfo = onInfo 49 | 50 | return viewController 51 | } 52 | 53 | func set(viewControllers: [UIViewController]) { 54 | setViewControllers(viewControllers, 55 | direction : .forward, 56 | animated : true, 57 | completion : nil) 58 | } 59 | } 60 | 61 | // MARK:- UIPageViewControllerDataSource 62 | extension OnboardingPageViewController: UIPageViewControllerDataSource { 63 | func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? { 64 | var index = (viewController as! OnboardingViewController).index 65 | index -= 1 66 | return showViewController(at: index) 67 | } 68 | 69 | func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? { 70 | var index = (viewController as! OnboardingViewController).index 71 | index += 1 72 | 73 | return showViewController(at: index) 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Source/Modules/Flows/Onboarding/Controllers/OnboardingViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OnboardingViewController.swift 3 | // CleanArchitectureWithCoordinatorPatternDemo 4 | // 5 | // Created by Maksim Kazachkov on 06.11.2017. 6 | // Copyright © 2017 Maksim Kazachkov. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class OnboardingViewController: UIViewController { 12 | 13 | // MARK:- Public variables 14 | var index = 0 15 | 16 | var image: UIImage! 17 | 18 | var isLast: Bool = false 19 | 20 | var onGoMarvel : CompletionBlock? 21 | var onInfo : CompletionBlock? 22 | 23 | // MARK:- IBOutlets 24 | @IBOutlet fileprivate weak var imageView: UIImageView! 25 | 26 | @IBOutlet fileprivate weak var pageControl: UIPageControl! 27 | 28 | @IBOutlet fileprivate weak var goMarvelButton: UIButton! 29 | 30 | // MARK:- UIViewController methods 31 | override func viewDidLoad() { 32 | super.viewDidLoad() 33 | setup() 34 | } 35 | 36 | override func viewWillAppear(_ animated: Bool) { 37 | super.viewWillAppear(animated) 38 | setupUI() 39 | } 40 | 41 | @IBAction func goMarvelPressed(_ sender: UIButton) { 42 | onGoMarvel?() 43 | } 44 | 45 | @IBAction func infoPressed(_ sender: UIButton) { 46 | onInfo?() 47 | } 48 | } 49 | 50 | //MARK:- Private methods 51 | extension OnboardingViewController { 52 | func setup() { 53 | setupUI() 54 | } 55 | 56 | func setupUI() { 57 | DispatchQueue.main.async { [unowned self] in 58 | self.imageView.image = self.image 59 | } 60 | pageControl.currentPage = index 61 | goMarvelButton.isHidden = !isLast 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Source/Modules/Flows/Onboarding/Controllers/OnboardingViewProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OnboardingViewProtocol.swift 3 | // CleanArchitectureWithCoordinatorPatternDemo 4 | // 5 | // Created by Maksim Kazachkov on 06.11.2017. 6 | // Copyright © 2017 Maksim Kazachkov. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol OnboardingViewProtocol: BaseViewProtocol { 12 | 13 | var onCompletion : CompletionBlock? { get set } 14 | var onInfo : CompletionBlock? { get set } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Source/Modules/Flows/Onboarding/OnboardingCoordinator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AuthorizationCoordinatorOutput.swift 3 | // Worker Dashy 4 | // 5 | // Created by Maksim Kazachkov on 17.08.17. 6 | // Copyright © 2017 Umbrella. All rights reserved. 7 | // 8 | 9 | class OnboardingCoordinator: BaseCoordinator, OnboardingCoordinatorOutput { 10 | 11 | var finishFlow: CompletionBlock? 12 | 13 | private let factory : OnboardingFactoryProtocol 14 | private let router : Routable 15 | 16 | init(with factory: OnboardingFactoryProtocol, router: Routable) { 17 | self.factory = factory 18 | self.router = router 19 | } 20 | } 21 | 22 | // MARK:- Coordinatable 23 | extension OnboardingCoordinator: Coordinatable { 24 | func start() { 25 | performOnboarding() 26 | } 27 | 28 | func performOnboarding() { 29 | let view = factory.makeOnboardingView() 30 | view.onCompletion = finishFlow 31 | view.onInfo = { [unowned self] in 32 | self.router.toPresent?.showAlert(title : "CREATE AWESOME STUFF WITH THE WORLD'S GREATEST COMIC API!", 33 | message : "The Marvel Comics API allows developers everywhere to access information about Marvel's vast library of comics—from what's coming up, to 70 years ago. ") 34 | } 35 | router.setRootModule(view, hideBar: true) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /CleanArchitectureWithCoordinatorPatternDemo/Source/Modules/Flows/Onboarding/OnboardingCoordinatorOutput.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AuthorizationCoordinatorOutput.swift 3 | // Worker Dashy 4 | // 5 | // Created by Maksim Kazachkov on 17.08.17. 6 | // Copyright © 2017 Umbrella. All rights reserved. 7 | // 8 | 9 | protocol OnboardingCoordinatorOutput: class { 10 | var finishFlow: CompletionBlock? { get set } 11 | } 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Kazachkov Maksim 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /Module VIP.xctemplate/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaksimKazachkov/iOS-Clean-Architecture-with-Coordinator-pattern/02c5f4ede8dc901e77dbd27eed0e55c858316047/Module VIP.xctemplate/.DS_Store -------------------------------------------------------------------------------- /Module VIP.xctemplate/TemplateIcon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaksimKazachkov/iOS-Clean-Architecture-with-Coordinator-pattern/02c5f4ede8dc901e77dbd27eed0e55c858316047/Module VIP.xctemplate/TemplateIcon.icns -------------------------------------------------------------------------------- /Module VIP.xctemplate/TemplateInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AllowedTypes 6 | 7 | public.swift-source 8 | 9 | Platforms 10 | 11 | com.apple.platform.iphoneos 12 | 13 | DefaultCompletionName 14 | SwiftVIPModule 15 | Description 16 | Basic Swift clean architecture module template. Creates ViewProtocol, ViewController, PresenterOutput, Presenter, Interactor, Assembly. 17 | Kind 18 | Xcode.IDEKit.TextSubstitutionFileTemplateKind 19 | MainTemplateFile 20 | ___FILEBASENAME___ 21 | SortOrder 22 | 1 23 | Options 24 | 25 | 26 | Description 27 | Name of module that you need create 28 | Default 29 | 30 | Identifier 31 | vipModuleName 32 | Name 33 | Module name 34 | Required 35 | YES 36 | Type 37 | text 38 | 39 | 40 | Default 41 | ___VARIABLE_vipModuleName___ 42 | Identifier 43 | productName 44 | Type 45 | static 46 | 47 | 48 | Template Author 49 | Kazachkov Maksim 50 | 51 | 52 | -------------------------------------------------------------------------------- /Module VIP.xctemplate/___FILEBASENAME___Assembly.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ___FILENAME___ 3 | // ___PROJECTNAME___ 4 | // 5 | // Created ___FULLUSERNAME___ on ___DATE___. 6 | // Copyright © ___YEAR___ ___ORGANIZATIONNAME___. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol ___VARIABLE_productName:identifier___Assemblable: ___VARIABLE_productName:identifier___ViewProtocol, ___VARIABLE_productName:identifier___PresenterOutput {} 12 | 13 | final class ___VARIABLE_productName:identifier___Assembly { 14 | static func assembly(with output: ___VARIABLE_productName:identifier___PresenterOutput) { 15 | let interactor = ___VARIABLE_productName:identifier___Interactor() 16 | let presenter = ___VARIABLE_productName:identifier___Presenter() 17 | 18 | interactor.presenter = presenter 19 | presenter.interactor = interactor 20 | presenter.output = output 21 | output.presenter = presenter 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Module VIP.xctemplate/___FILEBASENAME___Interactor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ___FILENAME___ 3 | // ___PROJECTNAME___ 4 | // 5 | // Created ___FULLUSERNAME___ on ___DATE___. 6 | // Copyright © ___YEAR___ ___ORGANIZATIONNAME___. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | final class ___VARIABLE_productName:identifier___Interactor { 12 | 13 | weak var presenter: ___VARIABLE_productName:identifier___Presenter? 14 | } 15 | -------------------------------------------------------------------------------- /Module VIP.xctemplate/___FILEBASENAME___Presenter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ___FILENAME___ 3 | // ___PROJECTNAME___ 4 | // 5 | // Created ___FULLUSERNAME___ on ___DATE___. 6 | // Copyright © ___YEAR___ ___ORGANIZATIONNAME___. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol ___VARIABLE_productName:identifier___PresenterInput: class { 12 | 13 | } 14 | 15 | protocol ___VARIABLE_productName:identifier___PresenterOutput: class { 16 | var presenter: ___VARIABLE_productName:identifier___PresenterInput? { get set } 17 | } 18 | 19 | final class ___VARIABLE_productName:identifier___Presenter { 20 | 21 | weak var output: ___VARIABLE_productName:identifier___PresenterOutput? 22 | 23 | var interactor: ___VARIABLE_productName:identifier___Interactor? 24 | } 25 | 26 | // MARK:- ___VARIABLE_productName:identifier___PresenterInput 27 | extension ___VARIABLE_productName:identifier___Presenter: ___VARIABLE_productName:identifier___PresenterInput { 28 | 29 | } 30 | -------------------------------------------------------------------------------- /Module VIP.xctemplate/___FILEBASENAME___ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ___FILENAME___ 3 | // ___PROJECTNAME___ 4 | // 5 | // Created ___FULLUSERNAME___ on ___DATE___. 6 | // Copyright © ___YEAR___ ___ORGANIZATIONNAME___. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ___VARIABLE_productName:identifier___ViewController: UIViewController, ___VARIABLE_productName:identifier___Assemblable { 12 | 13 | var presenter: ___VARIABLE_productName:identifier___PresenterInput? 14 | 15 | override func viewDidLoad() { 16 | super.viewDidLoad() 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /Module VIP.xctemplate/___FILEBASENAME___ViewProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ___FILENAME___ 3 | // ___PROJECTNAME___ 4 | // 5 | // Created ___FULLUSERNAME___ on ___DATE___. 6 | // Copyright © ___YEAR___ ___ORGANIZATIONNAME___. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol ___VARIABLE_productName:identifier___ViewProtocol: BaseViewProtocol { 12 | 13 | } 14 | 15 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment the next line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | target 'CleanArchitectureWithCoordinatorPatternDemo' do 5 | # Comment the next line if you're not using Swift and don't want to use dynamic frameworks 6 | use_frameworks! 7 | 8 | # Pods for CleanArchitectureWithCoordinatorPatternDemo 9 | 10 | pod 'Alamofire' 11 | pod 'AlamofireObjectMapper' 12 | pod 'ObjectMapper' 13 | pod 'Kingfisher' 14 | 15 | end 16 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Alamofire (4.5.1) 3 | - AlamofireObjectMapper (5.0.0): 4 | - Alamofire (~> 4.1) 5 | - ObjectMapper (~> 3.0) 6 | - Kingfisher (4.2.0) 7 | - ObjectMapper (3.0.0) 8 | 9 | DEPENDENCIES: 10 | - Alamofire 11 | - AlamofireObjectMapper 12 | - Kingfisher 13 | - ObjectMapper 14 | 15 | SPEC CHECKSUMS: 16 | Alamofire: 2d95912bf4c34f164fdfc335872e8c312acaea4a 17 | AlamofireObjectMapper: 5fafc816351cbbc0d486611aaeba7461c0cbad49 18 | Kingfisher: 9ee7e788d8ba07c3f21ce0d43f33cec310a4f781 19 | ObjectMapper: 92230db59bf8f341a5c3a3cf0b9fbdde3cf0d87f 20 | 21 | PODFILE CHECKSUM: 71da76bb26edc3613e718af2711d9f6b06ba1a28 22 | 23 | COCOAPODS: 1.3.1 24 | -------------------------------------------------------------------------------- /Pods/Alamofire/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014-2017 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 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/DispatchQueue+Alamofire.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DispatchQueue+Alamofire.swift 3 | // 4 | // Copyright (c) 2014-2017 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/Alamofire/Source/Notifications.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Notifications.swift 3 | // 4 | // Copyright (c) 2014-2017 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 | -------------------------------------------------------------------------------- /Pods/AlamofireObjectMapper/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Tristan Himmelman 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /Pods/Kingfisher/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Wei Wang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /Pods/Kingfisher/Sources/Box.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Box.swift 3 | // Kingfisher 4 | // 5 | // Created by WANG WEI on 2016/09/12. 6 | // Copyright © 2016年 Wei Wang. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class Box { 12 | let value: T 13 | init(value: T) { 14 | self.value = value 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Pods/Kingfisher/Sources/CacheSerializer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CacheSerializer.swift 3 | // Kingfisher 4 | // 5 | // Created by Wei Wang on 2016/09/02. 6 | // 7 | // Copyright (c) 2017 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import Foundation 28 | 29 | /// An `CacheSerializer` would be used to convert some data to an image object for 30 | /// retrieving from disk cache and vice versa for storing to disk cache. 31 | public protocol CacheSerializer { 32 | 33 | /// Get the serialized data from a provided image 34 | /// and optional original data for caching to disk. 35 | /// 36 | /// 37 | /// - parameter image: The image needed to be serialized. 38 | /// - parameter original: The original data which is just downloaded. 39 | /// If the image is retrieved from cache instead of 40 | /// downloaded, it will be `nil`. 41 | /// 42 | /// - returns: A data which will be stored to cache, or `nil` when no valid 43 | /// data could be serialized. 44 | func data(with image: Image, original: Data?) -> Data? 45 | 46 | /// Get an image deserialized from provided data. 47 | /// 48 | /// - parameter data: The data from which an image should be deserialized. 49 | /// - parameter options: Options for deserialization. 50 | /// 51 | /// - returns: An image deserialized or `nil` when no valid image 52 | /// could be deserialized. 53 | func image(with data: Data, options: KingfisherOptionsInfo?) -> Image? 54 | } 55 | 56 | 57 | /// `DefaultCacheSerializer` is a basic `CacheSerializer` used in default cache of 58 | /// Kingfisher. It could serialize and deserialize PNG, JEPG and GIF images. For 59 | /// image other than these formats, a normalized `pngRepresentation` will be used. 60 | public struct DefaultCacheSerializer: CacheSerializer { 61 | 62 | public static let `default` = DefaultCacheSerializer() 63 | private init() {} 64 | 65 | public func data(with image: Image, original: Data?) -> Data? { 66 | let imageFormat = original?.kf.imageFormat ?? .unknown 67 | 68 | let data: Data? 69 | switch imageFormat { 70 | case .PNG: data = image.kf.pngRepresentation() 71 | case .JPEG: data = image.kf.jpegRepresentation(compressionQuality: 1.0) 72 | case .GIF: data = image.kf.gifRepresentation() 73 | case .unknown: data = original ?? image.kf.normalized.kf.pngRepresentation() 74 | } 75 | 76 | return data 77 | } 78 | 79 | public func image(with data: Data, options: KingfisherOptionsInfo?) -> Image? { 80 | let options = options ?? KingfisherEmptyOptionsInfo 81 | return Kingfisher.image( 82 | data: data, 83 | scale: options.scaleFactor, 84 | preloadAllAnimationData: options.preloadAllAnimationData, 85 | onlyFirstFrame: options.onlyLoadFirstFrame) 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /Pods/Kingfisher/Sources/FormatIndicatedCacheSerializer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RequestModifier.swift 3 | // Kingfisher 4 | // 5 | // Created by Junyu Kuang on 5/28/17. 6 | // 7 | // Copyright (c) 2017 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import Foundation 28 | 29 | /// `FormatIndicatedCacheSerializer` let you indicate an image format for serialized caches. 30 | /// 31 | /// It could serialize and deserialize PNG, JEPG and GIF images. For 32 | /// image other than these formats, a normalized `pngRepresentation` will be used. 33 | /// 34 | /// Example: 35 | /// ```` 36 | /// private let profileImageSize = CGSize(width: 44, height: 44) 37 | /// 38 | /// private let imageProcessor = RoundCornerImageProcessor( 39 | /// cornerRadius: profileImageSize.width / 2, targetSize: profileImageSize) 40 | /// 41 | /// private let optionsInfo: KingfisherOptionsInfo = [ 42 | /// .cacheSerializer(FormatIndicatedCacheSerializer.png), 43 | /// .backgroundDecode, .processor(imageProcessor), .scaleFactor(UIScreen.main.scale)] 44 | /// 45 | /// extension UIImageView { 46 | /// func setProfileImage(with url: URL) { 47 | /// // Image will always cached as PNG format to preserve alpha channel for round rect. 48 | /// _ = kf.setImage(with: url, options: optionsInfo) 49 | /// } 50 | ///} 51 | /// ```` 52 | public struct FormatIndicatedCacheSerializer: CacheSerializer { 53 | 54 | public static let png = FormatIndicatedCacheSerializer(imageFormat: .PNG) 55 | public static let jpeg = FormatIndicatedCacheSerializer(imageFormat: .JPEG) 56 | public static let gif = FormatIndicatedCacheSerializer(imageFormat: .GIF) 57 | 58 | /// The indicated image format. 59 | private let imageFormat: ImageFormat 60 | 61 | public func data(with image: Image, original: Data?) -> Data? { 62 | 63 | func imageData(withFormat imageFormat: ImageFormat) -> Data? { 64 | switch imageFormat { 65 | case .PNG: return image.kf.pngRepresentation() 66 | case .JPEG: return image.kf.jpegRepresentation(compressionQuality: 1.0) 67 | case .GIF: return image.kf.gifRepresentation() 68 | case .unknown: return nil 69 | } 70 | } 71 | 72 | // generate data with indicated image format 73 | if let data = imageData(withFormat: imageFormat) { 74 | return data 75 | } 76 | 77 | let originalFormat = original?.kf.imageFormat ?? .unknown 78 | 79 | // generate data with original image's format 80 | if originalFormat != imageFormat, let data = imageData(withFormat: originalFormat) { 81 | return data 82 | } 83 | 84 | return original ?? image.kf.normalized.kf.pngRepresentation() 85 | } 86 | 87 | /// Same implementation as `DefaultCacheSerializer`. 88 | public func image(with data: Data, options: KingfisherOptionsInfo?) -> Image? { 89 | let options = options ?? KingfisherEmptyOptionsInfo 90 | return Kingfisher.image( 91 | data: data, 92 | scale: options.scaleFactor, 93 | preloadAllAnimationData: options.preloadAllAnimationData, 94 | onlyFirstFrame: options.onlyLoadFirstFrame) 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /Pods/Kingfisher/Sources/ImageTransition.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageTransition.swift 3 | // Kingfisher 4 | // 5 | // Created by Wei Wang on 15/9/18. 6 | // 7 | // Copyright (c) 2017 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #if os(macOS) 28 | // Not implemented for macOS and watchOS yet. 29 | 30 | import AppKit 31 | 32 | /// Image transition is not supported on macOS. 33 | public enum ImageTransition { 34 | case none 35 | var duration: TimeInterval { 36 | return 0 37 | } 38 | } 39 | 40 | #elseif os(watchOS) 41 | import UIKit 42 | /// Image transition is not supported on watchOS. 43 | public enum ImageTransition { 44 | case none 45 | var duration: TimeInterval { 46 | return 0 47 | } 48 | } 49 | #else 50 | import UIKit 51 | 52 | /** 53 | Transition effect which will be used when an image downloaded and set by `UIImageView` extension API in Kingfisher. 54 | You can assign an enum value with transition duration as an item in `KingfisherOptionsInfo` 55 | to enable the animation transition. 56 | 57 | Apple's UIViewAnimationOptions is used under the hood. 58 | For custom transition, you should specified your own transition options, animations and 59 | comletion handler as well. 60 | */ 61 | public enum ImageTransition { 62 | /// No animation transistion. 63 | case none 64 | 65 | /// Fade in the loaded image. 66 | case fade(TimeInterval) 67 | 68 | /// Flip from left transition. 69 | case flipFromLeft(TimeInterval) 70 | 71 | /// Flip from right transition. 72 | case flipFromRight(TimeInterval) 73 | 74 | /// Flip from top transition. 75 | case flipFromTop(TimeInterval) 76 | 77 | /// Flip from bottom transition. 78 | case flipFromBottom(TimeInterval) 79 | 80 | /// Custom transition. 81 | case custom(duration: TimeInterval, 82 | options: UIViewAnimationOptions, 83 | animations: ((UIImageView, UIImage) -> Void)?, 84 | completion: ((Bool) -> Void)?) 85 | 86 | var duration: TimeInterval { 87 | switch self { 88 | case .none: return 0 89 | case .fade(let duration): return duration 90 | 91 | case .flipFromLeft(let duration): return duration 92 | case .flipFromRight(let duration): return duration 93 | case .flipFromTop(let duration): return duration 94 | case .flipFromBottom(let duration): return duration 95 | 96 | case .custom(let duration, _, _, _): return duration 97 | } 98 | } 99 | 100 | var animationOptions: UIViewAnimationOptions { 101 | switch self { 102 | case .none: return [] 103 | case .fade(_): return .transitionCrossDissolve 104 | 105 | case .flipFromLeft(_): return .transitionFlipFromLeft 106 | case .flipFromRight(_): return .transitionFlipFromRight 107 | case .flipFromTop(_): return .transitionFlipFromTop 108 | case .flipFromBottom(_): return .transitionFlipFromBottom 109 | 110 | case .custom(_, let options, _, _): return options 111 | } 112 | } 113 | 114 | var animations: ((UIImageView, UIImage) -> Void)? { 115 | switch self { 116 | case .custom(_, _, let animations, _): return animations 117 | default: return { $0.image = $1 } 118 | } 119 | } 120 | 121 | var completion: ((Bool) -> Void)? { 122 | switch self { 123 | case .custom(_, _, _, let completion): return completion 124 | default: return nil 125 | } 126 | } 127 | } 128 | #endif 129 | -------------------------------------------------------------------------------- /Pods/Kingfisher/Sources/Kingfisher.h: -------------------------------------------------------------------------------- 1 | // 2 | // Kingfisher.h 3 | // Kingfisher 4 | // 5 | // Created by Wei Wang on 15/4/6. 6 | // 7 | // Copyright (c) 2017 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import 28 | 29 | //! Project version number for Kingfisher. 30 | FOUNDATION_EXPORT double KingfisherVersionNumber; 31 | 32 | //! Project version string for Kingfisher. 33 | FOUNDATION_EXPORT const unsigned char KingfisherVersionString[]; 34 | 35 | // In this header, you should import all the public headers of your framework using statements like #import 36 | 37 | 38 | -------------------------------------------------------------------------------- /Pods/Kingfisher/Sources/Kingfisher.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Kingfisher.swift 3 | // Kingfisher 4 | // 5 | // Created by Wei Wang on 16/9/14. 6 | // 7 | // Copyright (c) 2017 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import Foundation 28 | import ImageIO 29 | 30 | #if os(macOS) 31 | import AppKit 32 | public typealias Image = NSImage 33 | public typealias View = NSView 34 | public typealias Color = NSColor 35 | public typealias ImageView = NSImageView 36 | public typealias Button = NSButton 37 | #else 38 | import UIKit 39 | public typealias Image = UIImage 40 | public typealias Color = UIColor 41 | #if !os(watchOS) 42 | public typealias ImageView = UIImageView 43 | public typealias View = UIView 44 | public typealias Button = UIButton 45 | #endif 46 | #endif 47 | 48 | public final class Kingfisher { 49 | public let base: Base 50 | public init(_ base: Base) { 51 | self.base = base 52 | } 53 | } 54 | 55 | /** 56 | A type that has Kingfisher extensions. 57 | */ 58 | public protocol KingfisherCompatible { 59 | associatedtype CompatibleType 60 | var kf: CompatibleType { get } 61 | } 62 | 63 | public extension KingfisherCompatible { 64 | public var kf: Kingfisher { 65 | get { return Kingfisher(self) } 66 | } 67 | } 68 | 69 | extension Image: KingfisherCompatible { } 70 | #if !os(watchOS) 71 | extension ImageView: KingfisherCompatible { } 72 | extension Button: KingfisherCompatible { } 73 | #endif 74 | -------------------------------------------------------------------------------- /Pods/Kingfisher/Sources/Placeholder.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Placeholder.swift 3 | // Kingfisher 4 | // 5 | // Created by Tieme van Veen on 28/08/2017. 6 | // 7 | // Copyright (c) 2017 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #if os(macOS) 28 | import AppKit 29 | #else 30 | import UIKit 31 | #endif 32 | 33 | 34 | /// Represent a placeholder type which could be set while loading as well as 35 | /// loading finished without getting an image. 36 | public protocol Placeholder { 37 | 38 | /// How the placeholder should be added to a given image view. 39 | func add(to imageView: ImageView) 40 | 41 | /// How the placeholder should be removed from a given image view. 42 | func remove(from imageView: ImageView) 43 | } 44 | 45 | /// Default implementation of an image placeholder. The image will be set or 46 | /// reset directly for `image` property of the image view. 47 | extension Placeholder where Self: Image { 48 | 49 | /// How the placeholder should be added to a given image view. 50 | public func add(to imageView: ImageView) { imageView.image = self } 51 | 52 | /// How the placeholder should be removed from a given image view. 53 | public func remove(from imageView: ImageView) { imageView.image = nil } 54 | } 55 | 56 | extension Image: Placeholder {} 57 | 58 | /// Default implementation of an arbitrary view as placeholder. The view will be 59 | /// added as a subview when adding and be removed from its super view when removing. 60 | /// 61 | /// To use your customize View type as placeholder, simply let it conforming to 62 | /// `Placeholder` by `extension MyView: Placeholder {}`. 63 | extension Placeholder where Self: View { 64 | 65 | /// How the placeholder should be added to a given image view. 66 | public func add(to imageView: ImageView) { 67 | imageView.addSubview(self) 68 | 69 | self.translatesAutoresizingMaskIntoConstraints = false 70 | NSLayoutConstraint.activate([ 71 | NSLayoutConstraint(item: self, attribute: .centerX, relatedBy: .equal, toItem: imageView, attribute: .centerX, multiplier: 1, constant: 0), 72 | NSLayoutConstraint(item: self, attribute: .centerY, relatedBy: .equal, toItem: imageView, attribute: .centerY, multiplier: 1, constant: 0), 73 | NSLayoutConstraint(item: self, attribute: .height, relatedBy: .equal, toItem: imageView, attribute: .height, multiplier: 1, constant: 0), 74 | NSLayoutConstraint(item: self, attribute: .width, relatedBy: .equal, toItem: imageView, attribute: .width, multiplier: 1, constant: 0) 75 | ]) 76 | } 77 | 78 | /// How the placeholder should be removed from a given image view. 79 | public func remove(from imageView: ImageView) { 80 | self.removeFromSuperview() 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /Pods/Kingfisher/Sources/RequestModifier.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RequestModifier.swift 3 | // Kingfisher 4 | // 5 | // Created by Wei Wang on 2016/09/05. 6 | // 7 | // Copyright (c) 2017 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import Foundation 28 | 29 | /// Request modifier of image downloader. 30 | public protocol ImageDownloadRequestModifier { 31 | func modified(for request: URLRequest) -> URLRequest? 32 | } 33 | 34 | struct NoModifier: ImageDownloadRequestModifier { 35 | static let `default` = NoModifier() 36 | private init() {} 37 | func modified(for request: URLRequest) -> URLRequest? { 38 | return request 39 | } 40 | } 41 | 42 | public struct AnyModifier: ImageDownloadRequestModifier { 43 | 44 | let block: (URLRequest) -> URLRequest? 45 | 46 | public func modified(for request: URLRequest) -> URLRequest? { 47 | return block(request) 48 | } 49 | 50 | public init(modify: @escaping (URLRequest) -> URLRequest? ) { 51 | block = modify 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Pods/Kingfisher/Sources/Resource.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Resource.swift 3 | // Kingfisher 4 | // 5 | // Created by Wei Wang on 15/4/6. 6 | // 7 | // Copyright (c) 2017 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import Foundation 28 | 29 | 30 | /// `Resource` protocol defines how to download and cache a resource from network. 31 | public protocol Resource { 32 | /// The key used in cache. 33 | var cacheKey: String { get } 34 | 35 | /// The target image URL. 36 | var downloadURL: URL { get } 37 | } 38 | 39 | /** 40 | ImageResource is a simple combination of `downloadURL` and `cacheKey`. 41 | 42 | When passed to image view set methods, Kingfisher will try to download the target 43 | image from the `downloadURL`, and then store it with the `cacheKey` as the key in cache. 44 | */ 45 | public struct ImageResource: Resource { 46 | /// The key used in cache. 47 | public let cacheKey: String 48 | 49 | /// The target image URL. 50 | public let downloadURL: URL 51 | 52 | /** 53 | Create a resource. 54 | 55 | - parameter downloadURL: The target image URL. 56 | - parameter cacheKey: The cache key. If `nil`, Kingfisher will use the `absoluteString` of `downloadURL` as the key. 57 | 58 | - returns: A resource. 59 | */ 60 | public init(downloadURL: URL, cacheKey: String? = nil) { 61 | self.downloadURL = downloadURL 62 | self.cacheKey = cacheKey ?? downloadURL.absoluteString 63 | } 64 | } 65 | 66 | /** 67 | URL conforms to `Resource` in Kingfisher. 68 | The `absoluteString` of this URL is used as `cacheKey`. And the URL itself will be used as `downloadURL`. 69 | If you need customize the url and/or cache key, use `ImageResource` instead. 70 | */ 71 | extension URL: Resource { 72 | public var cacheKey: String { return absoluteString } 73 | public var downloadURL: URL { return self } 74 | } 75 | -------------------------------------------------------------------------------- /Pods/Kingfisher/Sources/ThreadHelper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ThreadHelper.swift 3 | // Kingfisher 4 | // 5 | // Created by Wei Wang on 15/10/9. 6 | // 7 | // Copyright (c) 2017 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import Foundation 28 | 29 | extension DispatchQueue { 30 | // This method will dispatch the `block` to self. 31 | // If `self` is the main queue, and current thread is main thread, the block 32 | // will be invoked immediately instead of being dispatched. 33 | func safeAsync(_ block: @escaping ()->()) { 34 | if self === DispatchQueue.main && Thread.isMainThread { 35 | block() 36 | } else { 37 | async { block() } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Alamofire (4.5.1) 3 | - AlamofireObjectMapper (5.0.0): 4 | - Alamofire (~> 4.1) 5 | - ObjectMapper (~> 3.0) 6 | - Kingfisher (4.2.0) 7 | - ObjectMapper (3.0.0) 8 | 9 | DEPENDENCIES: 10 | - Alamofire 11 | - AlamofireObjectMapper 12 | - Kingfisher 13 | - ObjectMapper 14 | 15 | SPEC CHECKSUMS: 16 | Alamofire: 2d95912bf4c34f164fdfc335872e8c312acaea4a 17 | AlamofireObjectMapper: 5fafc816351cbbc0d486611aaeba7461c0cbad49 18 | Kingfisher: 9ee7e788d8ba07c3f21ce0d43f33cec310a4f781 19 | ObjectMapper: 92230db59bf8f341a5c3a3cf0b9fbdde3cf0d87f 20 | 21 | PODFILE CHECKSUM: 71da76bb26edc3613e718af2711d9f6b06ba1a28 22 | 23 | COCOAPODS: 1.3.1 24 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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/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/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/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/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/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/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 = String(rgba[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 compsCount = color.cgColor.numberOfComponents 58 | let r: Int 59 | let g: Int 60 | var b: Int 61 | let a = Int(comps[compsCount - 1] * 255) 62 | if compsCount == 4 { // RGBA 63 | r = Int(comps[0] * 255) 64 | g = Int(comps[1] * 255) 65 | b = Int(comps[2] * 255) 66 | } else { // Grayscale 67 | r = Int(comps[0] * 255) 68 | g = Int(comps[0] * 255) 69 | b = Int(comps[0] * 255) 70 | } 71 | var hexString: String = "" 72 | if prefix { 73 | hexString = "#" 74 | } 75 | hexString += String(format: "%02X%02X%02X", r, g, b) 76 | 77 | if alpha { 78 | hexString += String(format: "%02X", a) 79 | } 80 | return hexString 81 | } 82 | 83 | fileprivate func getColor(hex: String) -> Object? { 84 | var red: CGFloat = 0.0 85 | var green: CGFloat = 0.0 86 | var blue: CGFloat = 0.0 87 | var alpha: CGFloat = 1.0 88 | 89 | let scanner = Scanner(string: hex) 90 | var hexValue: CUnsignedLongLong = 0 91 | if scanner.scanHexInt64(&hexValue) { 92 | switch (hex.characters.count) { 93 | case 3: 94 | red = CGFloat((hexValue & 0xF00) >> 8) / 15.0 95 | green = CGFloat((hexValue & 0x0F0) >> 4) / 15.0 96 | blue = CGFloat(hexValue & 0x00F) / 15.0 97 | case 4: 98 | red = CGFloat((hexValue & 0xF000) >> 12) / 15.0 99 | green = CGFloat((hexValue & 0x0F00) >> 8) / 15.0 100 | blue = CGFloat((hexValue & 0x00F0) >> 4) / 15.0 101 | alpha = CGFloat(hexValue & 0x000F) / 15.0 102 | case 6: 103 | red = CGFloat((hexValue & 0xFF0000) >> 16) / 255.0 104 | green = CGFloat((hexValue & 0x00FF00) >> 8) / 255.0 105 | blue = CGFloat(hexValue & 0x0000FF) / 255.0 106 | case 8: 107 | red = CGFloat((hexValue & 0xFF000000) >> 24) / 255.0 108 | green = CGFloat((hexValue & 0x00FF0000) >> 16) / 255.0 109 | blue = CGFloat((hexValue & 0x0000FF00) >> 8) / 255.0 110 | alpha = CGFloat(hexValue & 0x000000FF) / 255.0 111 | default: 112 | // Invalid RGB string, number of characters after '#' should be either 3, 4, 6 or 8 113 | return nil 114 | } 115 | } else { 116 | // "Scan hex error 117 | return nil 118 | } 119 | #if os(iOS) || os(tvOS) || os(watchOS) 120 | return UIColor(red: red, green: green, blue: blue, alpha: alpha) 121 | #else 122 | return NSColor(calibratedRed: red, green: green, blue: blue, alpha: alpha) 123 | #endif 124 | } 125 | } 126 | #endif 127 | -------------------------------------------------------------------------------- /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 | public extension DateFormatter { 32 | public convenience init(withFormat format : String, locale : String) { 33 | self.init() 34 | self.locale = Locale(identifier: locale) 35 | dateFormat = format 36 | } 37 | } 38 | 39 | open class ISO8601DateTransform: DateFormatterTransform { 40 | 41 | static let reusableISODateFormatter = DateFormatter(withFormat: "yyyy-MM-dd'T'HH:mm:ssZZZZZ", locale: "en_US_POSIX") 42 | 43 | public init() { 44 | super.init(dateFormatter: ISO8601DateTransform.reusableISODateFormatter) 45 | } 46 | } 47 | 48 | -------------------------------------------------------------------------------- /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 94 | let value = value, 95 | case let number as NSNumber = value 96 | else { 97 | return nil 98 | } 99 | 100 | if T.self == Int.self, let x = Int(exactly: number.int64Value) { 101 | return T.init(x) 102 | } 103 | if T.self == Int8.self, let x = Int8(exactly: number.int64Value) { 104 | return T.init(x) 105 | } 106 | if T.self == Int16.self, let x = Int16(exactly: number.int64Value) { 107 | return T.init(x) 108 | } 109 | if T.self == Int32.self, let x = Int32(exactly: number.int64Value) { 110 | return T.init(x) 111 | } 112 | if T.self == Int64.self, let x = Int64(exactly: number.int64Value) { 113 | return T.init(x) 114 | } 115 | 116 | return nil 117 | } 118 | 119 | /// Convert any value to `UnsignedInteger`. 120 | private func toUnsignedInteger(_ value: Any?) -> T? { 121 | guard 122 | let value = value, 123 | case let number as NSNumber = value 124 | else { 125 | return nil 126 | } 127 | 128 | if T.self == UInt.self, let x = UInt(exactly: number.uint64Value) { 129 | return T.init(x) 130 | } 131 | if T.self == UInt8.self, let x = UInt8(exactly: number.uint64Value) { 132 | return T.init(x) 133 | } 134 | if T.self == UInt16.self, let x = UInt16(exactly: number.uint64Value) { 135 | return T.init(x) 136 | } 137 | if T.self == UInt32.self, let x = UInt32(exactly: number.uint64Value) { 138 | return T.init(x) 139 | } 140 | if T.self == UInt64.self, let x = UInt64(exactly: number.uint64Value) { 141 | return T.init(x) 142 | } 143 | 144 | return nil 145 | } 146 | -------------------------------------------------------------------------------- /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.0): \($0.1 ?? "nil")" }.joined(separator: "\n") 65 | return "Got an error while mapping.\n\(infoString)" 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /Pods/ObjectMapper/Sources/Mappable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Mappable.swift 3 | // ObjectMapper 4 | // 5 | // Created by Scott Hoyt on 10/25/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 | /// BaseMappable should not be implemented directly. Mappable or StaticMappable should be used instead 32 | public protocol BaseMappable { 33 | /// This function is where all variable mappings should occur. It is executed by Mapper during the mapping (serialization and deserialization) process. 34 | mutating func mapping(map: Map) 35 | } 36 | 37 | public protocol Mappable: BaseMappable { 38 | /// This function can be used to validate JSON prior to mapping. Return nil to cancel mapping at this point 39 | init?(map: Map) 40 | } 41 | 42 | public protocol StaticMappable: BaseMappable { 43 | /// This is function that can be used to: 44 | /// 1) provide an existing cached object to be used for mapping 45 | /// 2) return an object of another class (which conforms to BaseMappable) to be used for mapping. For instance, you may inspect the JSON to infer the type of object that should be used for any given mapping 46 | static func objectForMapping(map: Map) -> BaseMappable? 47 | } 48 | 49 | public extension BaseMappable { 50 | 51 | /// Initializes object from a JSON String 52 | public init?(JSONString: String, context: MapContext? = nil) { 53 | if let obj: Self = Mapper(context: context).map(JSONString: JSONString) { 54 | self = obj 55 | } else { 56 | return nil 57 | } 58 | } 59 | 60 | /// Initializes object from a JSON Dictionary 61 | public init?(JSON: [String: Any], context: MapContext? = nil) { 62 | if let obj: Self = Mapper(context: context).map(JSON: JSON) { 63 | self = obj 64 | } else { 65 | return nil 66 | } 67 | } 68 | 69 | /// Returns the JSON Dictionary for the object 70 | public func toJSON() -> [String: Any] { 71 | return Mapper().toJSON(self) 72 | } 73 | 74 | /// Returns the JSON String for the object 75 | public func toJSONString(prettyPrint: Bool = false) -> String? { 76 | return Mapper().toJSONString(self, prettyPrint: prettyPrint) 77 | } 78 | } 79 | 80 | public extension Array where Element: BaseMappable { 81 | 82 | /// Initialize Array from a JSON String 83 | public init?(JSONString: String, context: MapContext? = nil) { 84 | if let obj: [Element] = Mapper(context: context).mapArray(JSONString: JSONString) { 85 | self = obj 86 | } else { 87 | return nil 88 | } 89 | } 90 | 91 | /// Initialize Array from a JSON Array 92 | public init(JSONArray: [[String: Any]], context: MapContext? = nil) { 93 | let obj: [Element] = Mapper(context: context).mapArray(JSONArray: JSONArray) 94 | self = obj 95 | } 96 | 97 | /// Returns the JSON Array 98 | public func toJSON() -> [[String: Any]] { 99 | return Mapper().toJSONArray(self) 100 | } 101 | 102 | /// Returns the JSON String for the object 103 | public func toJSONString(prettyPrint: Bool = false) -> String? { 104 | return Mapper().toJSONString(self, prettyPrint: prettyPrint) 105 | } 106 | } 107 | 108 | public extension Set where Element: BaseMappable { 109 | 110 | /// Initializes a set from a JSON String 111 | public init?(JSONString: String, context: MapContext? = nil) { 112 | if let obj: Set = Mapper(context: context).mapSet(JSONString: JSONString) { 113 | self = obj 114 | } else { 115 | return nil 116 | } 117 | } 118 | 119 | /// Initializes a set from JSON 120 | public init?(JSONArray: [[String: Any]], context: MapContext? = nil) { 121 | guard let obj = Mapper(context: context).mapSet(JSONArray: JSONArray) as Set? else { 122 | return nil 123 | } 124 | self = obj 125 | } 126 | 127 | /// Returns the JSON Set 128 | public func toJSON() -> [[String: Any]] { 129 | return Mapper().toJSONSet(self) 130 | } 131 | 132 | /// Returns the JSON String for the object 133 | public func toJSONString(prettyPrint: Bool = false) -> String? { 134 | return Mapper().toJSONString(self, prettyPrint: prettyPrint) 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /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 | } else if let number = value as? NSNumber { 41 | return NSDecimalNumber(decimal: number.decimalValue) 42 | } else if let double = value as? Double { 43 | return NSDecimalNumber(floatLiteral: double) 44 | } 45 | return nil 46 | } 47 | 48 | open func transformToJSON(_ value: NSDecimalNumber?) -> String? { 49 | guard let value = value else { return nil } 50 | return value.description 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /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/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/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 | private let allowedCharacterSet: CharacterSet 36 | 37 | /** 38 | Initializes the URLTransform with an option to encode URL strings before converting them to an NSURL 39 | - parameter shouldEncodeUrlString: when true (the default) the string is encoded before passing 40 | to `NSURL(string:)` 41 | - returns: an initialized transformer 42 | */ 43 | public init(shouldEncodeURLString: Bool = true, allowedCharacterSet: CharacterSet = .urlQueryAllowed) { 44 | self.shouldEncodeURLString = shouldEncodeURLString 45 | self.allowedCharacterSet = allowedCharacterSet 46 | } 47 | 48 | open func transformFromJSON(_ value: Any?) -> URL? { 49 | guard let URLString = value as? String else { return nil } 50 | 51 | if !shouldEncodeURLString { 52 | return URL(string: URLString) 53 | } 54 | 55 | guard let escapedURLString = URLString.addingPercentEncoding(withAllowedCharacters: allowedCharacterSet) else { 56 | return nil 57 | } 58 | return URL(string: escapedURLString) 59 | } 60 | 61 | open func transformToJSON(_ value: URL?) -> String? { 62 | if let URL = value { 63 | return URL.absoluteString 64 | } 65 | return nil 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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.5.1 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/AlamofireObjectMapper/AlamofireObjectMapper-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_AlamofireObjectMapper : NSObject 3 | @end 4 | @implementation PodsDummy_AlamofireObjectMapper 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/AlamofireObjectMapper/AlamofireObjectMapper-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/AlamofireObjectMapper/AlamofireObjectMapper-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 AlamofireObjectMapperVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char AlamofireObjectMapperVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Pods/Target Support Files/AlamofireObjectMapper/AlamofireObjectMapper.modulemap: -------------------------------------------------------------------------------- 1 | framework module AlamofireObjectMapper { 2 | umbrella header "AlamofireObjectMapper-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/AlamofireObjectMapper/AlamofireObjectMapper.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/AlamofireObjectMapper 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/Alamofire" "$PODS_CONFIGURATION_BUILD_DIR/ObjectMapper" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public" 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}/AlamofireObjectMapper 10 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 11 | SKIP_INSTALL = YES 12 | -------------------------------------------------------------------------------- /Pods/Target Support Files/AlamofireObjectMapper/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 | 5.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Kingfisher/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.2.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Kingfisher/Kingfisher-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Kingfisher : NSObject 3 | @end 4 | @implementation PodsDummy_Kingfisher 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Kingfisher/Kingfisher-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/Kingfisher/Kingfisher-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 "Kingfisher.h" 14 | 15 | FOUNDATION_EXPORT double KingfisherVersionNumber; 16 | FOUNDATION_EXPORT const unsigned char KingfisherVersionString[]; 17 | 18 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Kingfisher/Kingfisher.modulemap: -------------------------------------------------------------------------------- 1 | framework module Kingfisher { 2 | umbrella header "Kingfisher-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Kingfisher/Kingfisher.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/Kingfisher 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public" 4 | OTHER_LDFLAGS = -framework "CFNetwork" 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}/Kingfisher 10 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 11 | SKIP_INSTALL = YES 12 | SWIFT_VERSION = 4.0 13 | -------------------------------------------------------------------------------- /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 | 3.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/ObjectMapper/ObjectMapper-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_ObjectMapper : NSObject 3 | @end 4 | @implementation PodsDummy_ObjectMapper 5 | @end 6 | -------------------------------------------------------------------------------- /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/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/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/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 = 4.0 12 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-CleanArchitectureWithCoordinatorPatternDemo/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/Pods-CleanArchitectureWithCoordinatorPatternDemo/Pods-CleanArchitectureWithCoordinatorPatternDemo-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## Alamofire 5 | 6 | Copyright (c) 2014-2017 Alamofire Software Foundation (http://alamofire.org/) 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in 16 | all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | THE SOFTWARE. 25 | 26 | 27 | ## AlamofireObjectMapper 28 | 29 | The MIT License (MIT) 30 | 31 | Copyright (c) 2015 Tristan Himmelman 32 | 33 | Permission is hereby granted, free of charge, to any person obtaining a copy 34 | of this software and associated documentation files (the "Software"), to deal 35 | in the Software without restriction, including without limitation the rights 36 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 37 | copies of the Software, and to permit persons to whom the Software is 38 | furnished to do so, subject to the following conditions: 39 | 40 | The above copyright notice and this permission notice shall be included in all 41 | copies or substantial portions of the Software. 42 | 43 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 44 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 45 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 46 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 47 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 48 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 49 | SOFTWARE. 50 | 51 | 52 | 53 | ## Kingfisher 54 | 55 | The MIT License (MIT) 56 | 57 | Copyright (c) 2017 Wei Wang 58 | 59 | Permission is hereby granted, free of charge, to any person obtaining a copy 60 | of this software and associated documentation files (the "Software"), to deal 61 | in the Software without restriction, including without limitation the rights 62 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 63 | copies of the Software, and to permit persons to whom the Software is 64 | furnished to do so, subject to the following conditions: 65 | 66 | The above copyright notice and this permission notice shall be included in all 67 | copies or substantial portions of the Software. 68 | 69 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 70 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 71 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 72 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 73 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 74 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 75 | SOFTWARE. 76 | 77 | 78 | 79 | ## ObjectMapper 80 | 81 | The MIT License (MIT) 82 | Copyright (c) 2014 Hearst 83 | 84 | 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: 85 | 86 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 87 | 88 | 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. 89 | 90 | Generated by CocoaPods - https://cocoapods.org 91 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-CleanArchitectureWithCoordinatorPatternDemo/Pods-CleanArchitectureWithCoordinatorPatternDemo-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_CleanArchitectureWithCoordinatorPatternDemo : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_CleanArchitectureWithCoordinatorPatternDemo 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-CleanArchitectureWithCoordinatorPatternDemo/Pods-CleanArchitectureWithCoordinatorPatternDemo-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_CleanArchitectureWithCoordinatorPatternDemoVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_CleanArchitectureWithCoordinatorPatternDemoVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-CleanArchitectureWithCoordinatorPatternDemo/Pods-CleanArchitectureWithCoordinatorPatternDemo.debug.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/Alamofire" "$PODS_CONFIGURATION_BUILD_DIR/AlamofireObjectMapper" "$PODS_CONFIGURATION_BUILD_DIR/Kingfisher" "$PODS_CONFIGURATION_BUILD_DIR/ObjectMapper" 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/AlamofireObjectMapper/AlamofireObjectMapper.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/Kingfisher/Kingfisher.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/ObjectMapper/ObjectMapper.framework/Headers" 6 | OTHER_LDFLAGS = $(inherited) -framework "Alamofire" -framework "AlamofireObjectMapper" -framework "Kingfisher" -framework "ObjectMapper" 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_PODFILE_DIR_PATH = ${SRCROOT}/. 11 | PODS_ROOT = ${SRCROOT}/Pods 12 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-CleanArchitectureWithCoordinatorPatternDemo/Pods-CleanArchitectureWithCoordinatorPatternDemo.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_CleanArchitectureWithCoordinatorPatternDemo { 2 | umbrella header "Pods-CleanArchitectureWithCoordinatorPatternDemo-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-CleanArchitectureWithCoordinatorPatternDemo/Pods-CleanArchitectureWithCoordinatorPatternDemo.release.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/Alamofire" "$PODS_CONFIGURATION_BUILD_DIR/AlamofireObjectMapper" "$PODS_CONFIGURATION_BUILD_DIR/Kingfisher" "$PODS_CONFIGURATION_BUILD_DIR/ObjectMapper" 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/AlamofireObjectMapper/AlamofireObjectMapper.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/Kingfisher/Kingfisher.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/ObjectMapper/ObjectMapper.framework/Headers" 6 | OTHER_LDFLAGS = $(inherited) -framework "Alamofire" -framework "AlamofireObjectMapper" -framework "Kingfisher" -framework "ObjectMapper" 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_PODFILE_DIR_PATH = ${SRCROOT}/. 11 | PODS_ROOT = ${SRCROOT}/Pods 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # iOS Clean Architecture with Coordinator pattern 2 | 3 | When you decide to use clean architecture in your project, it is very tired create new modules, because you need create at least 5 files for each. Imagine that you need to create 6 modules...I was suffering this problem, and this is the reason why I've created this template. It's very useful for me and I hope that for you too. 4 | 5 | ## How to install 6 | 7 | ### Using script (easy) 8 | Only need execute this command in terminal: `sudo swift install.swift` 9 | You should be this output message: 10 | ************************************ 11 | ✅ Xcode🔨 template installed succesfully 👌🏻. Enjoy it 👨🏻‍💻 12 | ************************************ 13 | 14 | ## How to uninstall 15 | 16 | ### Using script (easy) 17 | Only need execute this command in terminal: `sudo swift uninstall.swift` 18 | You should be this output message: 19 | 20 | ************************************ 21 | ✅ Xcode🔨 template uninstalled succesfully 👌🏻. 22 | ************************************ 23 | 24 | If all it's ok you now could find your template in Xcode. 25 | 26 | ### Manual 27 | Go to Application folder, browse to the Xcode application icon. Right-click it and choose 'Show Package Contents'. Then browse to: 28 | `Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/Xcode/Templates/Project Templates/iOS/Application` and add "Module VIP.xctemplate" file. Now you can find your template in Xcode. 29 | 30 | 31 | ## Generated code 32 | This template generates all files that you need to create a new clean module. All generated code is Swift 4. 33 | 34 | ## References 35 | - reference to my article 36 | 37 | ## Contributed 38 | This is an open source project, so feel free to contribute. How? 39 | 40 | - Open an [issue](link). 41 | - Send feedback via [email](maksimkn.94@gmail.com). 42 | - Propose your own fixes, suggestions and open a pull request with the changes. 43 | 44 | ## Author 45 | 46 | * Kazachkov Maksim 47 | -------------------------------------------------------------------------------- /install.swift: -------------------------------------------------------------------------------- 1 | // 2 | // main.swift 3 | // InstallVIPERTemplate 4 | // 5 | // Created by Juanpe Catalán on 17/02/2017. 6 | // Copyright © 2017 Juanpe Catalán. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | let templateName = "Module VIP.xctemplate" 12 | let destinationRelativePath = "/Platforms/iPhoneOS.platform/Developer/Library/Xcode/Templates/Project Templates/iOS/Application" 13 | 14 | func printInConsole(_ message:Any){ 15 | print("\n") 16 | print("************************************") 17 | print("\(message)") 18 | print("************************************") 19 | } 20 | 21 | func moveTemplate(){ 22 | 23 | let fileManager = FileManager.default 24 | let destinationPath = bash(command: "xcode-select", arguments: ["--print-path"]).appending(destinationRelativePath) 25 | do { 26 | if !fileManager.fileExists(atPath:"\(destinationPath)/\(templateName)"){ 27 | 28 | try fileManager.copyItem(atPath: templateName, toPath: "\(destinationPath)/\(templateName)") 29 | 30 | printInConsole("✅ Xcode🔨 template installed succesfully 👌🏻. Enjoy it 👨🏻‍💻") 31 | 32 | } 33 | else { 34 | try _ = fileManager.replaceItemAt(URL(fileURLWithPath:"\(destinationPath)/\(templateName)"), withItemAt: URL(fileURLWithPath:templateName)) 35 | 36 | printInConsole("✅ Xcode🔨 template already exists. So has been replaced succesfully 👌🏻. Enjoy it 👨🏻‍💻") 37 | } 38 | } 39 | catch let error as NSError { 40 | printInConsole("❌ Ooops! Something went wrong 🔥😡 : \(error.localizedFailureReason!)") 41 | } 42 | } 43 | 44 | func shell(launchPath: String, arguments: [String]) -> String 45 | { 46 | let task = Process() 47 | task.launchPath = launchPath 48 | task.arguments = arguments 49 | 50 | let pipe = Pipe() 51 | task.standardOutput = pipe 52 | task.launch() 53 | 54 | let data = pipe.fileHandleForReading.readDataToEndOfFile() 55 | let output = String(data: data, encoding: String.Encoding.utf8)! 56 | if output.characters.count > 0 { 57 | //remove newline character. 58 | let lastIndex = output.index(before: output.endIndex) 59 | return String(output[output.startIndex ..< lastIndex]) 60 | } 61 | return output 62 | } 63 | 64 | func bash(command: String, arguments: [String]) -> String { 65 | let whichPathForCommand = shell(launchPath: "/bin/bash", arguments: [ "-l", "-c", "which \(command)" ]) 66 | return shell(launchPath: whichPathForCommand, arguments: arguments) 67 | } 68 | 69 | moveTemplate() 70 | -------------------------------------------------------------------------------- /uninstall.swift: -------------------------------------------------------------------------------- 1 | // 2 | // main.swift 3 | // InstallVIPTemplate 4 | // 5 | // Created by Kazachkov Maksim on 29/10/2017. 6 | // Copyright © 2017 Kazachkov Maksim. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | let templateName = "Module VIP.xctemplate" 12 | let destinationRelativePath = "/Platforms/iPhoneOS.platform/Developer/Library/Xcode/Templates/Project Templates/iOS/Application" 13 | 14 | func printInConsole(_ message:Any){ 15 | print("\n") 16 | print("************************************") 17 | print("\(message)") 18 | print("************************************") 19 | } 20 | 21 | func removeTemplate(){ 22 | 23 | let fileManager = FileManager.default 24 | let destinationPath = bash(command: "xcode-select", arguments: ["--print-path"]).appending(destinationRelativePath) 25 | do { 26 | let path = "\(destinationPath)/\(templateName)" 27 | if fileManager.fileExists(atPath: path){ 28 | try _ = fileManager.removeItem(atPath: "\(destinationPath)/\(templateName)") 29 | printInConsole("✅ Xcode🔨 template uninstalled succesfully 👌🏻.") 30 | } 31 | else { 32 | printInConsole("❌ Xcode🔨 template does not exist at \(path).") 33 | } 34 | 35 | } 36 | catch let error as NSError { 37 | printInConsole("❌ Ooops! Something went wrong 🔥😡 : \(error.localizedFailureReason!)") 38 | } 39 | } 40 | 41 | func shell(launchPath: String, arguments: [String]) -> String 42 | { 43 | let task = Process() 44 | task.launchPath = launchPath 45 | task.arguments = arguments 46 | 47 | let pipe = Pipe() 48 | task.standardOutput = pipe 49 | task.launch() 50 | 51 | let data = pipe.fileHandleForReading.readDataToEndOfFile() 52 | let output = String(data: data, encoding: String.Encoding.utf8)! 53 | if output.characters.count > 0 { 54 | //remove newline character. 55 | let lastIndex = output.index(before: output.endIndex) 56 | return String(output[output.startIndex ..< lastIndex]) 57 | } 58 | return output 59 | } 60 | 61 | func bash(command: String, arguments: [String]) -> String { 62 | let whichPathForCommand = shell(launchPath: "/bin/bash", arguments: [ "-l", "-c", "which \(command)" ]) 63 | return shell(launchPath: whichPathForCommand, arguments: arguments) 64 | } 65 | 66 | removeTemplate() 67 | --------------------------------------------------------------------------------