├── Coordinator.pdf ├── Podfile ├── CoordinatorExample ├── Classes │ ├── Model │ │ ├── Entities │ │ │ └── Item.swift │ │ ├── Services │ │ │ ├── AuthService.swift │ │ │ └── ItemsService.swift │ │ └── Storage │ │ │ └── AuthStorage.swift │ ├── Presentation Layer │ │ ├── Flows │ │ │ ├── MainFlow │ │ │ │ ├── ItemDetailsModule │ │ │ │ │ ├── ItemDetailsModule.swift │ │ │ │ │ ├── ViewModel │ │ │ │ │ │ └── ItemDetailsViewModel.swift │ │ │ │ │ ├── ItemDetailsModuleAssembly.swift │ │ │ │ │ ├── ViewController │ │ │ │ │ │ └── ItemDetailsViewController.swift │ │ │ │ │ └── View │ │ │ │ │ │ └── ItemDetailsView.swift │ │ │ │ ├── ItemCreationFlow │ │ │ │ │ ├── ItemCreationModule │ │ │ │ │ │ ├── ItemCreationModule.swift │ │ │ │ │ │ ├── ItemCreationModuleAssembly.swift │ │ │ │ │ │ ├── ViewModel │ │ │ │ │ │ │ └── ItemCreationViewModel.swift │ │ │ │ │ │ ├── ViewController │ │ │ │ │ │ │ └── ItemCreationViewController.swift │ │ │ │ │ │ └── View │ │ │ │ │ │ │ └── ItemCreationView.swift │ │ │ │ │ ├── ItemCreationCoordinator.swift │ │ │ │ │ └── ItemCreationCoordinatorAssembly.swift │ │ │ │ ├── ItemsListModule │ │ │ │ │ ├── ItemsListModule.swift │ │ │ │ │ ├── ItemsListModuleAssembly.swift │ │ │ │ │ ├── View │ │ │ │ │ │ └── ItemsListView.swift │ │ │ │ │ ├── ViewModel │ │ │ │ │ │ └── ItemsListViewModel.swift │ │ │ │ │ └── ViewController │ │ │ │ │ │ └── ItemsListViewController.swift │ │ │ │ ├── MainCoordinatorAssembly.swift │ │ │ │ └── MainCoordinator.swift │ │ │ ├── AuthFlow │ │ │ │ ├── AuthModule │ │ │ │ │ ├── AuthModule.swift │ │ │ │ │ ├── ViewModel │ │ │ │ │ │ └── AuthViewModel.swift │ │ │ │ │ ├── AuthModuleAssembly.swift │ │ │ │ │ ├── View │ │ │ │ │ │ └── AuthView.swift │ │ │ │ │ └── ViewController │ │ │ │ │ │ └── AuthViewController.swift │ │ │ │ ├── AuthCoordinator.swift │ │ │ │ └── AuthCoordinatorAssembly.swift │ │ │ ├── AppFlow │ │ │ │ ├── AppCoordinatorAssembly.swift │ │ │ │ └── AppCoordinator.swift │ │ │ └── Routers │ │ │ │ ├── NavigationRouter.swift │ │ │ │ └── AppRouter.swift │ │ ├── Coordinator │ │ │ ├── Protocols │ │ │ │ ├── DeepLinkOption.swift │ │ │ │ ├── Presentable.swift │ │ │ │ ├── Coordinator.swift │ │ │ │ └── Routable.swift │ │ │ └── Implementations │ │ │ │ ├── BaseCoordinator.swift │ │ │ │ └── Presentable+Navigation.swift │ │ └── Helpers │ │ │ └── ViewHolder │ │ │ └── ViewHolder.swift │ └── Application Layer │ │ ├── CEDeepLinkOption.swift │ │ ├── AppDelegate+Coordinator.swift │ │ ├── ServicesAssembly.swift │ │ └── AppDelegate.swift ├── Supporting Files │ ├── Info.plist │ └── Base.lproj │ │ └── LaunchScreen.storyboard └── Resources │ └── Images │ └── Assets.xcassets │ └── AppIcon.appiconset │ └── Contents.json ├── README.md ├── CoordinatorExample.xcodeproj ├── project.xcworkspace │ └── contents.xcworkspacedata ├── xcuserdata │ └── arychkov.xcuserdatad │ │ └── xcschemes │ │ └── xcschememanagement.plist └── project.pbxproj ├── CoordinatorExample.xcworkspace └── contents.xcworkspacedata ├── Podfile.lock ├── LICENSE └── .gitignore /Coordinator.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Moonko/CoordinatorExample/HEAD/Coordinator.pdf -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, '9.0' 2 | 3 | target 'CoordinatorExample' do 4 | pod 'Swinject' 5 | end 6 | -------------------------------------------------------------------------------- /CoordinatorExample/Classes/Model/Entities/Item.swift: -------------------------------------------------------------------------------- 1 | struct Item { 2 | 3 | let name: String 4 | } 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Coordinator Example 2 | Application coordinator example made for CocoaHeads SPb 04.05.2018 3 | -------------------------------------------------------------------------------- /CoordinatorExample/Classes/Presentation Layer/Flows/MainFlow/ItemDetailsModule/ItemDetailsModule.swift: -------------------------------------------------------------------------------- 1 | protocol ItemDetailsModule: Presentable { 2 | 3 | typealias Input = Item 4 | } 5 | -------------------------------------------------------------------------------- /CoordinatorExample/Classes/Presentation Layer/Flows/AuthFlow/AuthModule/AuthModule.swift: -------------------------------------------------------------------------------- 1 | protocol AuthModule: Presentable { 2 | 3 | typealias Completion = () -> Void 4 | 5 | var onFinish: Completion? { get set } 6 | } 7 | -------------------------------------------------------------------------------- /CoordinatorExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /CoordinatorExample/Classes/Presentation Layer/Flows/MainFlow/ItemCreationFlow/ItemCreationModule/ItemCreationModule.swift: -------------------------------------------------------------------------------- 1 | protocol ItemCreationModule: Presentable { 2 | 3 | typealias Completion = () -> Void 4 | 5 | var onFinish: Completion? { get set } 6 | } 7 | -------------------------------------------------------------------------------- /CoordinatorExample/Classes/Presentation Layer/Coordinator/Protocols/DeepLinkOption.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public protocol DeepLinkOption { 4 | 5 | static func build(with userActivity: NSUserActivity) -> DeepLinkOption? 6 | static func build(with dict: [String: AnyObject]?) -> DeepLinkOption? 7 | } 8 | -------------------------------------------------------------------------------- /CoordinatorExample.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /CoordinatorExample/Classes/Presentation Layer/Coordinator/Protocols/Presentable.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | protocol Presentable { 4 | 5 | var toPresent: UIViewController? { get } 6 | } 7 | 8 | extension UIViewController: Presentable { 9 | 10 | var toPresent: UIViewController? { 11 | return self 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /CoordinatorExample/Classes/Presentation Layer/Coordinator/Protocols/Coordinator.swift: -------------------------------------------------------------------------------- 1 | protocol Coordinator: class { 2 | 3 | var router: Routable { get } 4 | 5 | func start() 6 | func start(with deepLink: DeepLinkOption?) 7 | } 8 | 9 | extension Coordinator { 10 | 11 | func start() { 12 | start(with: nil) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Swinject (2.2.0) 3 | 4 | DEPENDENCIES: 5 | - Swinject 6 | 7 | SPEC REPOS: 8 | https://github.com/CocoaPods/Specs.git: 9 | - Swinject 10 | 11 | SPEC CHECKSUMS: 12 | Swinject: 4ca48bca7de5f398229a26abe1ac902b9f3ee541 13 | 14 | PODFILE CHECKSUM: 0a17140135b32a780567b27bce6a15141b16489b 15 | 16 | COCOAPODS: 1.5.0 17 | -------------------------------------------------------------------------------- /CoordinatorExample/Classes/Presentation Layer/Flows/MainFlow/ItemDetailsModule/ViewModel/ItemDetailsViewModel.swift: -------------------------------------------------------------------------------- 1 | protocol ItemDetailsViewModel { 2 | 3 | var itemName: String { get } 4 | } 5 | 6 | final class ItemDetailsViewModelImpl: ItemDetailsViewModel { 7 | 8 | let itemName: String 9 | 10 | init(item: Item) { 11 | self.itemName = item.name 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /CoordinatorExample/Classes/Application Layer/CEDeepLinkOption.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | enum CEDeepLinkOption: DeepLinkOption { 4 | 5 | case itemCreation 6 | 7 | static func build(with dict: [String : AnyObject]?) -> DeepLinkOption? { 8 | return nil 9 | } 10 | 11 | static func build(with userActivity: NSUserActivity) -> DeepLinkOption? { 12 | return nil 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /CoordinatorExample/Classes/Presentation Layer/Flows/AuthFlow/AuthModule/ViewModel/AuthViewModel.swift: -------------------------------------------------------------------------------- 1 | protocol AuthViewModel: class { 2 | 3 | typealias AuthBlock = () -> Void 4 | 5 | func auth(_ onFinish: AuthBlock?) 6 | } 7 | 8 | final class AuthViewModelImpl: AuthViewModel { 9 | 10 | var authService: AuthService! 11 | 12 | func auth(_ onFinish: AuthBlock?) { 13 | authService.auth() 14 | onFinish?() 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /CoordinatorExample/Classes/Presentation Layer/Helpers/ViewHolder/ViewHolder.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | public protocol ViewHolder: class { 4 | 5 | associatedtype RootViewType: UIView 6 | } 7 | 8 | public extension ViewHolder where Self: UIViewController { 9 | 10 | public var rootView: RootViewType { 11 | guard let rootView = view as? RootViewType else { 12 | fatalError("Excpected \(RootViewType.description()) as rootView. Now \(type(of: view))") 13 | } 14 | return rootView 15 | } 16 | } 17 | 18 | -------------------------------------------------------------------------------- /CoordinatorExample/Classes/Application Layer/AppDelegate+Coordinator.swift: -------------------------------------------------------------------------------- 1 | import Swinject 2 | 3 | extension AppDelegate { 4 | 5 | func makeAppCoordinator() -> Coordinator { 6 | let rootAssembler = Assembler( 7 | [ 8 | ServicesAssembly(), 9 | AppCoordinatorAssembly() 10 | ], 11 | container: Container() 12 | ) 13 | return rootAssembler.resolver.resolve( 14 | AppCoordinator.self, 15 | arguments: rootAssembler, window)! 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /CoordinatorExample/Classes/Presentation Layer/Flows/AuthFlow/AuthModule/AuthModuleAssembly.swift: -------------------------------------------------------------------------------- 1 | import Swinject 2 | 3 | struct AuthModuleAssembly: Assembly { 4 | 5 | func assemble(container: Container) { 6 | container.register(AuthModule.self) { resolver in 7 | let controller = AuthViewController() 8 | let viewModel = AuthViewModelImpl() 9 | viewModel.authService = resolver.resolve(AuthService.self) 10 | controller.viewModel = viewModel 11 | return controller 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /CoordinatorExample/Classes/Presentation Layer/Flows/MainFlow/ItemsListModule/ItemsListModule.swift: -------------------------------------------------------------------------------- 1 | protocol ItemsListModule: Presentable { 2 | 3 | typealias ViewDidAppearBlock = () -> Void 4 | typealias ItemCreationBlock = () -> Void 5 | typealias ItemSelectBlock = (Item) -> Void 6 | typealias LogoutBlock = () -> Void 7 | 8 | var onViewDidAppear: ViewDidAppearBlock? { get set } 9 | var onItemCreate: ItemCreationBlock? { get set } 10 | var onItemSelect: ItemSelectBlock? { get set } 11 | var onLogout: LogoutBlock? { get set } 12 | } 13 | -------------------------------------------------------------------------------- /CoordinatorExample/Classes/Presentation Layer/Flows/MainFlow/ItemDetailsModule/ItemDetailsModuleAssembly.swift: -------------------------------------------------------------------------------- 1 | import Swinject 2 | 3 | struct ItemDetailsModuleAssembly: Assembly { 4 | 5 | func assemble(container: Container) { 6 | container.register(ItemDetailsModule.self) { (_, input: ItemDetailsModule.Input) in 7 | let controller = ItemDetailsViewController() 8 | let viewModel = ItemDetailsViewModelImpl(item: input) 9 | controller.viewModel = viewModel 10 | return controller 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /CoordinatorExample/Classes/Presentation Layer/Flows/MainFlow/ItemCreationFlow/ItemCreationModule/ItemCreationModuleAssembly.swift: -------------------------------------------------------------------------------- 1 | import Swinject 2 | 3 | struct ItemCreationModuleAssembly: Assembly { 4 | 5 | func assemble(container: Container) { 6 | container.register(ItemCreationModule.self) { resolver in 7 | let controller = ItemCreationViewController() 8 | let viewModel = ItemCreationViewModelImpl() 9 | viewModel.itemsService = resolver.resolve(ItemsService.self) 10 | controller.viewModel = viewModel 11 | return controller 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /CoordinatorExample/Classes/Presentation Layer/Flows/MainFlow/ItemsListModule/ItemsListModuleAssembly.swift: -------------------------------------------------------------------------------- 1 | import Swinject 2 | 3 | struct ItemsListModuleAssembly: Assembly { 4 | 5 | func assemble(container: Container) { 6 | container.register(ItemsListModule.self) { resolver in 7 | let controller = ItemsListViewController() 8 | let viewModel = ItemsListViewModelImpl() 9 | viewModel.itemsService = resolver.resolve(ItemsService.self) 10 | viewModel.authService = resolver.resolve(AuthService.self) 11 | controller.viewModel = viewModel 12 | return controller 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /CoordinatorExample.xcodeproj/xcuserdata/arychkov.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | CoordinatorExample.xcscheme 8 | 9 | orderHint 10 | 2 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 0C506E532097014500E67227 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /CoordinatorExample/Classes/Model/Services/AuthService.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | protocol AuthStateProvider { 4 | 5 | var authState: Auth { get } 6 | } 7 | 8 | protocol AuthService { 9 | 10 | func auth() 11 | func logout() 12 | } 13 | 14 | final class AuthServiceImpl: AuthService, AuthStateProvider { 15 | 16 | var storage: AuthStorage! 17 | 18 | // MARK: AuthService 19 | 20 | func auth() { 21 | storage.saveAuth(true) 22 | } 23 | 24 | func logout() { 25 | storage.saveAuth(false) 26 | } 27 | 28 | // MARK: AuthStateProvider 29 | 30 | var authState: Auth { 31 | return storage.obtainAuth() 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /CoordinatorExample/Classes/Presentation Layer/Flows/MainFlow/ItemCreationFlow/ItemCreationModule/ViewModel/ItemCreationViewModel.swift: -------------------------------------------------------------------------------- 1 | protocol ItemCreationViewModel { 2 | 3 | typealias ItemCreatedBlock = () -> Void 4 | 5 | var onItemCreated: ItemCreatedBlock? { get set } 6 | 7 | func createItem(named name: String) 8 | } 9 | 10 | final class ItemCreationViewModelImpl: ItemCreationViewModel { 11 | 12 | var itemsService: ItemsService! 13 | 14 | var onItemCreated: ItemCreatedBlock? 15 | 16 | func createItem(named name: String) { 17 | let newItem = Item(name: name) 18 | itemsService.saveItem(newItem) 19 | onItemCreated?() 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /CoordinatorExample/Classes/Presentation Layer/Flows/MainFlow/ItemCreationFlow/ItemCreationCoordinator.swift: -------------------------------------------------------------------------------- 1 | protocol ItemCreationCoordinator: Coordinator { 2 | 3 | typealias Completion = () -> Void 4 | 5 | var onFinish: Completion? { get set } 6 | } 7 | 8 | final class ItemCreationCoordinatorImpl: BaseCoordinator, ItemCreationCoordinator { 9 | 10 | var onFinish: Completion? 11 | 12 | override func start(with option: DeepLinkOption?) { 13 | showItemCreation() 14 | } 15 | 16 | private func showItemCreation() { 17 | var module = assembler.resolver.resolve(ItemCreationModule.self) 18 | module?.onFinish = { [weak self] in 19 | self?.onFinish?() 20 | } 21 | router.setRootModule(module, animated: false) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /CoordinatorExample/Classes/Presentation Layer/Flows/MainFlow/ItemCreationFlow/ItemCreationCoordinatorAssembly.swift: -------------------------------------------------------------------------------- 1 | import Swinject 2 | 3 | struct ItemCreationCoordinatorAssembly: Assembly { 4 | 5 | func assemble(container: Container) { 6 | container.register(ItemCreationCoordinator.self) { (_, parentAssembler: Assembler) in 7 | let assembler = Assembler( 8 | [ 9 | ItemCreationModuleAssembly() 10 | ], 11 | parent: parentAssembler 12 | ) 13 | let router = NavigationRouter(rootController: UINavigationController()) 14 | let coordinator = ItemCreationCoordinatorImpl(assembler: assembler, router: router) 15 | return coordinator 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /CoordinatorExample/Classes/Presentation Layer/Flows/AuthFlow/AuthCoordinator.swift: -------------------------------------------------------------------------------- 1 | protocol AuthCoordinator: Coordinator { 2 | 3 | typealias Completion = (DeepLinkOption?) -> Void 4 | 5 | var onFinish: Completion? { get set } 6 | } 7 | 8 | final class AuthCoordinatorImpl: BaseCoordinator, AuthCoordinator { 9 | 10 | var onFinish: Completion? 11 | 12 | private var deepLink: DeepLinkOption? 13 | 14 | override func start(with deepLink: DeepLinkOption?) { 15 | self.deepLink = deepLink 16 | 17 | showAuthModule() 18 | } 19 | 20 | func showAuthModule() { 21 | var module = assembler.resolver.resolve(AuthModule.self) 22 | module?.onFinish = { [weak self] in 23 | self?.onFinish?(self?.deepLink) 24 | } 25 | router.push(module) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /CoordinatorExample/Classes/Presentation Layer/Flows/MainFlow/ItemsListModule/View/ItemsListView.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | final class ItemsListView: UIView { 4 | 5 | let itemsTableView: UITableView = { 6 | let tableView = UITableView() 7 | tableView.rowHeight = UITableViewAutomaticDimension 8 | tableView.estimatedRowHeight = 44 9 | tableView.tableFooterView = UIView() 10 | return tableView 11 | }() 12 | 13 | override init(frame: CGRect) { 14 | super.init(frame: frame) 15 | 16 | addSubview(itemsTableView) 17 | } 18 | 19 | required init?(coder aDecoder: NSCoder) { 20 | fatalError("init(coder:) has not been implemented") 21 | } 22 | 23 | override func layoutSubviews() { 24 | super.layoutSubviews() 25 | 26 | itemsTableView.frame = bounds 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /CoordinatorExample/Classes/Presentation Layer/Flows/AppFlow/AppCoordinatorAssembly.swift: -------------------------------------------------------------------------------- 1 | import Swinject 2 | 3 | struct AppCoordinatorAssembly: Assembly { 4 | 5 | func assemble(container: Container) { 6 | container.register(AppCoordinator.self) { (resolver, parentAssembler: Assembler, window: UIWindow?) in 7 | let assembler = Assembler( 8 | [ 9 | AuthCoordinatorAssembly(), 10 | MainCoordinatorAssembly() 11 | ], 12 | parent: parentAssembler 13 | ) 14 | let router = AppRouter(window: window) 15 | let coordinator = AppCoordinatorImpl(assembler: assembler, router: router) 16 | coordinator.authStateProvider = resolver.resolve(AuthStateProvider.self) 17 | return coordinator 18 | } 19 | } 20 | } 21 | 22 | -------------------------------------------------------------------------------- /CoordinatorExample/Classes/Presentation Layer/Flows/AuthFlow/AuthModule/View/AuthView.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | final class AuthView: UIView { 4 | 5 | let authButton: UIButton = { 6 | let button = UIButton(type: .system) 7 | button.setTitle("Log In", for: .normal) 8 | button.titleLabel?.font = .systemFont(ofSize: 20) 9 | return button 10 | }() 11 | 12 | override init(frame: CGRect) { 13 | super.init(frame: frame) 14 | 15 | backgroundColor = .white 16 | 17 | addSubview(authButton) 18 | } 19 | 20 | required init?(coder aDecoder: NSCoder) { 21 | fatalError("init(coder:) has not been implemented") 22 | } 23 | 24 | override func layoutSubviews() { 25 | super.layoutSubviews() 26 | 27 | authButton.sizeToFit() 28 | authButton.center = center 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /CoordinatorExample/Classes/Model/Services/ItemsService.swift: -------------------------------------------------------------------------------- 1 | protocol ItemsListener { 2 | 3 | func itemsWereUpdated() 4 | } 5 | 6 | protocol ItemsService { 7 | 8 | var items: [Item] { get } 9 | 10 | func addListener(_ listener: ItemsListener, with id: String) 11 | func removeListener(with id: String) 12 | 13 | func saveItem(_ item: Item) 14 | } 15 | 16 | final class ItemsServiceImpl: ItemsService { 17 | 18 | private(set) var items: [Item] = [] 19 | 20 | private var listeners = [String: ItemsListener]() 21 | 22 | func addListener(_ listener: ItemsListener, with id: String) { 23 | listeners[id] = listener 24 | } 25 | 26 | func removeListener(with id: String) { 27 | listeners.removeValue(forKey: id) 28 | } 29 | 30 | func saveItem(_ item: Item) { 31 | items.append(item) 32 | listeners.values.forEach { $0.itemsWereUpdated() } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /CoordinatorExample/Classes/Model/Storage/AuthStorage.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | typealias Auth = Bool 4 | 5 | protocol AuthStorage { 6 | 7 | func saveAuth(_ newValue: Auth) 8 | func obtainAuth() -> Auth 9 | } 10 | 11 | final class AuthStorageImpl: AuthStorage { 12 | 13 | private var auth: Auth { 14 | get { 15 | return UserDefaults.standard.bool(forKey: Constants.authenticatedKey) 16 | } 17 | set { 18 | UserDefaults.standard.set(newValue, forKey: Constants.authenticatedKey) 19 | } 20 | } 21 | 22 | func saveAuth(_ newValue: Auth) { 23 | auth = newValue 24 | } 25 | 26 | func obtainAuth() -> Auth { 27 | return auth 28 | } 29 | } 30 | 31 | private extension AuthStorageImpl { 32 | 33 | struct Constants { 34 | 35 | static let authenticatedKey = "authenticated" 36 | 37 | private init() { } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /CoordinatorExample/Classes/Presentation Layer/Flows/AuthFlow/AuthCoordinatorAssembly.swift: -------------------------------------------------------------------------------- 1 | import Swinject 2 | 3 | struct AuthCoordinatorAssembly: Assembly { 4 | 5 | func assemble(container: Container) { 6 | container.register(AuthCoordinator.self) { (resolver, parentAssembler: Assembler) in 7 | let assembler = Assembler( 8 | [ 9 | AuthModuleAssembly() 10 | ], 11 | parent: parentAssembler 12 | ) 13 | let navigationController = UINavigationController() 14 | if #available(iOS 11, *) { 15 | navigationController.navigationBar.prefersLargeTitles = true 16 | } 17 | let router = NavigationRouter(rootController: navigationController) 18 | let coordinator = AuthCoordinatorImpl(assembler: assembler, router: router) 19 | return coordinator 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /CoordinatorExample/Classes/Application Layer/ServicesAssembly.swift: -------------------------------------------------------------------------------- 1 | import Swinject 2 | 3 | struct ServicesAssembly: Assembly { 4 | 5 | func assemble(container: Container) { 6 | registerAuthServices(in: container) 7 | registerItemsServices(in: container) 8 | } 9 | 10 | private func registerAuthServices(in container: Container) { 11 | let authService = AuthServiceImpl() 12 | authService.storage = AuthStorageImpl() 13 | 14 | container.register(AuthService.self) { _ in 15 | return authService 16 | }.inObjectScope(.container) 17 | 18 | container.register(AuthStateProvider.self) { _ in 19 | return authService 20 | }.inObjectScope(.container) 21 | } 22 | 23 | private func registerItemsServices(in container: Container) { 24 | container.register(ItemsService.self) { _ in 25 | return ItemsServiceImpl() 26 | }.inObjectScope(.container) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /CoordinatorExample/Classes/Presentation Layer/Flows/MainFlow/ItemDetailsModule/ViewController/ItemDetailsViewController.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | final class ItemDetailsViewController: UIViewController, 4 | ItemDetailsModule, 5 | ViewHolder { 6 | 7 | var viewModel: ItemDetailsViewModel! 8 | 9 | override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { 10 | super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) 11 | 12 | title = "Item details" 13 | } 14 | 15 | required init?(coder aDecoder: NSCoder) { 16 | fatalError("init(coder:) has not been implemented") 17 | } 18 | 19 | // MARK: Lifecycle 20 | 21 | typealias RootViewType = ItemDetailsView 22 | 23 | override func loadView() { 24 | view = ItemDetailsView() 25 | } 26 | 27 | override func viewDidLoad() { 28 | super.viewDidLoad() 29 | 30 | rootView.configure(with: viewModel.itemName) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /CoordinatorExample/Classes/Presentation Layer/Flows/MainFlow/ItemDetailsModule/View/ItemDetailsView.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | final class ItemDetailsView: UIView { 4 | 5 | private let nameLabel: UILabel = { 6 | let label = UILabel() 7 | label.font = .systemFont(ofSize: 30) 8 | label.numberOfLines = 0 9 | return label 10 | }() 11 | 12 | override init(frame: CGRect) { 13 | super.init(frame: frame) 14 | 15 | backgroundColor = .white 16 | 17 | addSubview(nameLabel) 18 | } 19 | 20 | required init?(coder aDecoder: NSCoder) { 21 | fatalError("init(coder:) has not been implemented") 22 | } 23 | 24 | func configure(with itemName: String) { 25 | nameLabel.text = itemName 26 | setNeedsLayout() 27 | } 28 | 29 | override func layoutSubviews() { 30 | super.layoutSubviews() 31 | 32 | nameLabel.frame.size = nameLabel.sizeThatFits(bounds.size) 33 | nameLabel.center = center 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /CoordinatorExample/Classes/Presentation Layer/Flows/MainFlow/MainCoordinatorAssembly.swift: -------------------------------------------------------------------------------- 1 | import Swinject 2 | 3 | struct MainCoordinatorAssembly: Assembly { 4 | 5 | func assemble(container: Container) { 6 | container.register(MainCoordinator.self) { (resolver, parentAssembler: Assembler) in 7 | let assembler = Assembler( 8 | [ 9 | ItemsListModuleAssembly(), 10 | ItemDetailsModuleAssembly(), 11 | ItemCreationCoordinatorAssembly() 12 | ], 13 | parent: parentAssembler 14 | ) 15 | let navigationController = UINavigationController() 16 | if #available(iOS 11, *) { 17 | navigationController.navigationBar.prefersLargeTitles = true 18 | } 19 | let router = NavigationRouter(rootController: navigationController) 20 | let coordinator = MainCoordinatorImpl(assembler: assembler, router: router) 21 | return coordinator 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /CoordinatorExample/Classes/Presentation Layer/Coordinator/Implementations/BaseCoordinator.swift: -------------------------------------------------------------------------------- 1 | import Swinject 2 | 3 | open class BaseCoordinator: Coordinator { 4 | 5 | var childCoordinators: [Coordinator] = [] 6 | 7 | let router: Routable 8 | let assembler: Assembler 9 | 10 | init(assembler: Assembler, router: Routable) { 11 | self.assembler = assembler 12 | self.router = router 13 | } 14 | 15 | open func start(with option: DeepLinkOption?) { } 16 | 17 | func addDependency(_ coordinator: Coordinator) { 18 | guard !childCoordinators.contains(where: { $0 === coordinator }) 19 | else { return } 20 | childCoordinators.append(coordinator) 21 | } 22 | 23 | func removeDependency(_ coordinator: Coordinator?) { 24 | guard let indexToRemove = childCoordinators.index(where: { $0 === coordinator }) 25 | else { return } 26 | childCoordinators.remove(at: indexToRemove) 27 | } 28 | 29 | func removeAllDependencies() { 30 | childCoordinators.removeAll() 31 | } 32 | } 33 | 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Andrei Rychkov 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 | -------------------------------------------------------------------------------- /CoordinatorExample/Classes/Presentation Layer/Coordinator/Protocols/Routable.swift: -------------------------------------------------------------------------------- 1 | protocol Routable: Presentable { 2 | 3 | func present(_ module: Presentable?) 4 | func present(_ module: Presentable?, animated: Bool) 5 | 6 | func push(_ module: Presentable?) 7 | func push(_ module: Presentable?, animated: Bool) 8 | 9 | func popModule() 10 | func popModule(animated: Bool) 11 | 12 | func dismissModule() 13 | func dismissModule(animated: Bool, completion: (() -> Void)?) 14 | 15 | func setRootModule(_ module: Presentable?) 16 | func setRootModule(_ module: Presentable?, animated: Bool) 17 | } 18 | 19 | extension Routable { 20 | 21 | func present(_ module: Presentable?) { 22 | present(module, animated: true) 23 | } 24 | 25 | func push(_ module: Presentable?) { 26 | push(module, animated: true) 27 | } 28 | 29 | func popModule() { 30 | popModule(animated: true) 31 | } 32 | 33 | func dismissModule() { 34 | dismissModule(animated: true, completion: nil) 35 | } 36 | 37 | func setRootModule(_ module: Presentable?) { 38 | setRootModule(module, animated: true) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /CoordinatorExample/Classes/Presentation Layer/Flows/MainFlow/ItemsListModule/ViewModel/ItemsListViewModel.swift: -------------------------------------------------------------------------------- 1 | protocol ItemsListViewModel { 2 | 3 | typealias UpdateBlock = () -> Void 4 | 5 | var items: [Item] { get } 6 | var onItemsUpdate: UpdateBlock? { get set } 7 | 8 | func startUpdatingItems() 9 | func stopUpdatingItems() 10 | func logout() 11 | } 12 | 13 | final class ItemsListViewModelImpl: ItemsListViewModel, ItemsListener { 14 | 15 | var onItemsUpdate: UpdateBlock? 16 | 17 | var itemsService: ItemsService! 18 | var authService: AuthService! 19 | 20 | var items: [Item] { 21 | return itemsService.items 22 | } 23 | 24 | func startUpdatingItems() { 25 | itemsService.addListener(self, with: Constants.listenerKey) 26 | } 27 | 28 | func stopUpdatingItems() { 29 | itemsService.removeListener(with: Constants.listenerKey) 30 | } 31 | 32 | func itemsWereUpdated() { 33 | onItemsUpdate?() 34 | } 35 | 36 | func logout() { 37 | authService.logout() 38 | } 39 | } 40 | 41 | private extension ItemsListViewModelImpl { 42 | 43 | struct Constants { 44 | 45 | static let listenerKey = "items_list_listener" 46 | 47 | private init() { } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /CoordinatorExample/Classes/Presentation Layer/Flows/AuthFlow/AuthModule/ViewController/AuthViewController.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | final class AuthViewController: UIViewController, 4 | AuthModule, 5 | ViewHolder { 6 | 7 | var onFinish: Completion? 8 | 9 | var viewModel: AuthViewModel! 10 | 11 | override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { 12 | super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) 13 | 14 | title = "Auth" 15 | } 16 | 17 | required init?(coder aDecoder: NSCoder) { 18 | fatalError("init(coder:) has not been implemented") 19 | } 20 | 21 | // MARK: ViewHolder 22 | 23 | typealias RootViewType = AuthView 24 | 25 | // MARK: Lifecycle 26 | 27 | override func loadView() { 28 | view = AuthView() 29 | } 30 | 31 | override func viewDidLoad() { 32 | super.viewDidLoad() 33 | 34 | bindAuthButton() 35 | } 36 | 37 | // MARK: Bindings 38 | 39 | private func bindAuthButton() { 40 | rootView.authButton.addTarget(self, action: #selector(auth), for: .touchUpInside) 41 | } 42 | 43 | @objc 44 | private func auth() { 45 | viewModel.auth { [weak self] in 46 | self?.onFinish?() 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /CoordinatorExample/Supporting Files/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 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIRequiredDeviceCapabilities 26 | 27 | armv7 28 | 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | 33 | UISupportedInterfaceOrientations~ipad 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationPortraitUpsideDown 37 | UIInterfaceOrientationLandscapeLeft 38 | UIInterfaceOrientationLandscapeRight 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /CoordinatorExample/Classes/Presentation Layer/Flows/AppFlow/AppCoordinator.swift: -------------------------------------------------------------------------------- 1 | import Swinject 2 | 3 | protocol AppCoordinator: Coordinator { } 4 | 5 | final class AppCoordinatorImpl: BaseCoordinator, AppCoordinator { 6 | 7 | var authStateProvider: AuthStateProvider! 8 | 9 | override func start(with option: DeepLinkOption?) { 10 | switch authStateProvider.authState { 11 | case false: 12 | runAuthFlow(with: option) 13 | case true: 14 | runMainFlow(with: option) 15 | } 16 | } 17 | 18 | private func runAuthFlow(with deepLink: DeepLinkOption? = nil) { 19 | let coordinator = assembler.resolver.resolve(AuthCoordinator.self, argument: assembler)! 20 | coordinator.onFinish = { [weak coordinator, weak self] deepLink in 21 | self?.removeDependency(coordinator) 22 | self?.runMainFlow(with: deepLink) 23 | } 24 | addDependency(coordinator) 25 | router.setRootModule(coordinator.router) 26 | coordinator.start(with: deepLink) 27 | } 28 | 29 | private func runMainFlow(with deepLink: DeepLinkOption?) { 30 | let coordinator = assembler.resolver.resolve(MainCoordinator.self, argument: assembler)! 31 | coordinator.onLogout = { [weak coordinator, weak self] in 32 | self?.removeDependency(coordinator) 33 | self?.runAuthFlow() 34 | } 35 | addDependency(coordinator) 36 | router.setRootModule(coordinator.router) 37 | coordinator.start(with: deepLink) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /CoordinatorExample/Classes/Application Layer/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | @UIApplicationMain 4 | class AppDelegate: UIResponder, UIApplicationDelegate { 5 | 6 | var window: UIWindow? 7 | 8 | private lazy var appCoordinator: Coordinator = makeAppCoordinator() 9 | 10 | func application( 11 | _ application: UIApplication, 12 | didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]? 13 | ) -> Bool { 14 | 15 | window = UIWindow(frame: UIScreen.main.bounds) 16 | let notification = launchOptions?[.remoteNotification] as? [String: AnyObject] 17 | // Replace this line with the following one to check deep link 18 | let deepLink = CEDeepLinkOption.build(with: notification) 19 | // let deepLink = CEDeepLinkOption.itemCreation 20 | appCoordinator.start(with: deepLink) 21 | window?.makeKeyAndVisible() 22 | 23 | return true 24 | } 25 | 26 | func application(_ application: UIApplication, 27 | didReceiveRemoteNotification userInfo: [AnyHashable: Any]) { 28 | let dict = userInfo as? [String: AnyObject] 29 | let deepLink = CEDeepLinkOption.build(with: dict) 30 | appCoordinator.start(with: deepLink) 31 | } 32 | 33 | func application(_ application: UIApplication, continue userActivity: NSUserActivity, 34 | restorationHandler: @escaping ([Any]?) -> Void) -> Bool { 35 | let deepLink = CEDeepLinkOption.build(with: userActivity) 36 | appCoordinator.start(with: deepLink) 37 | return true 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /CoordinatorExample/Classes/Presentation Layer/Flows/MainFlow/ItemCreationFlow/ItemCreationModule/ViewController/ItemCreationViewController.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | final class ItemCreationViewController: UIViewController, 4 | ItemCreationModule, 5 | ViewHolder { 6 | 7 | var onFinish: Completion? 8 | 9 | var viewModel: ItemCreationViewModel! 10 | 11 | // MARK: View lifecycle 12 | 13 | typealias RootViewType = ItemCreationView 14 | 15 | override func loadView() { 16 | view = ItemCreationView() 17 | } 18 | 19 | override func viewDidLoad() { 20 | super.viewDidLoad() 21 | 22 | bindItemCreation() 23 | 24 | navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(handleCancelButtonTap)) 25 | 26 | rootView.createButton.addTarget(self, action: #selector(handleCreateButtonTap), for: .touchUpInside) 27 | } 28 | 29 | override func viewDidAppear(_ animated: Bool) { 30 | super.viewDidAppear(animated) 31 | 32 | rootView.nameTextField.becomeFirstResponder() 33 | } 34 | 35 | // MARK: Bindings 36 | 37 | private func bindItemCreation() { 38 | viewModel.onItemCreated = { [weak self] in 39 | self?.onFinish?() 40 | } 41 | } 42 | 43 | @objc 44 | private func handleCancelButtonTap() { 45 | onFinish?() 46 | } 47 | 48 | @objc 49 | private func handleCreateButtonTap() { 50 | let itemName = rootView.nameTextField.text ?? "" 51 | viewModel.createItem(named: itemName) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /CoordinatorExample/Classes/Presentation Layer/Flows/Routers/NavigationRouter.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | struct NavigationRouter: Routable { 4 | 5 | private let rootController: UINavigationController 6 | 7 | init(rootController: UINavigationController) { 8 | self.rootController = rootController 9 | } 10 | 11 | // MARK: Routable 12 | 13 | var toPresent: UIViewController? { 14 | return rootController 15 | } 16 | 17 | func present(_ module: Presentable?, animated: Bool) { 18 | guard let controllerToPresent = module?.toPresent else { return } 19 | rootController.present(controllerToPresent, animated: animated, completion: nil) 20 | } 21 | 22 | func dismissModule(animated: Bool, completion: (() -> Void)?) { 23 | rootController.dismiss(animated: animated, completion: completion) 24 | } 25 | 26 | func push(_ module: Presentable?, animated: Bool) { 27 | guard let controllerToPush = module?.toPresent else { return } 28 | // Won't show the same screen twice 29 | if let topController = rootController.topViewController, 30 | type(of: controllerToPush) == type(of: topController) { return } 31 | rootController.pushViewController(controllerToPush, animated: animated) 32 | } 33 | 34 | func popModule(animated: Bool) { 35 | rootController.popViewController(animated: animated) 36 | } 37 | 38 | func setRootModule(_ module: Presentable?, animated: Bool) { 39 | guard let controllerToSet = module?.toPresent else { return } 40 | rootController.setViewControllers([controllerToSet], animated: animated) 41 | } 42 | } 43 | 44 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /CoordinatorExample/Classes/Presentation Layer/Coordinator/Implementations/Presentable+Navigation.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | extension Presentable { 4 | 5 | func embededInNavigation() -> Presentable? { 6 | guard let controllerToPresent = toPresent else { return nil } 7 | let navController = UINavigationController(rootViewController: controllerToPresent) 8 | return navController 9 | } 10 | 11 | func withRemovedBackItem() -> Presentable? { 12 | guard let controllerToPresent = toPresent else { return nil } 13 | controllerToPresent.navigationItem.hidesBackButton = true 14 | return controllerToPresent 15 | } 16 | 17 | func withDismissItem() -> Presentable? { 18 | guard let controllerToPresent = toPresent else { return nil } 19 | controllerToPresent.navigationItem.leftBarButtonItem = UIBarButtonItem( 20 | barButtonSystemItem: .cancel, 21 | target: controllerToPresent, 22 | action: #selector(UIViewController.dismissModal) 23 | ) 24 | return controllerToPresent 25 | } 26 | 27 | func withDisabledLargeTitle() -> Presentable? { 28 | guard let controllerToPresent = toPresent else { return nil } 29 | if #available(iOS 11, *) { 30 | controllerToPresent.navigationItem.largeTitleDisplayMode = .never 31 | } 32 | return controllerToPresent 33 | } 34 | 35 | func withHiddenBottomBar() -> Presentable? { 36 | toPresent?.hidesBottomBarWhenPushed = true 37 | return self 38 | } 39 | } 40 | 41 | private extension UIViewController { 42 | 43 | @objc 44 | func dismissModal() { 45 | dismiss(animated: true, completion: nil) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /CoordinatorExample/Classes/Presentation Layer/Flows/MainFlow/ItemCreationFlow/ItemCreationModule/View/ItemCreationView.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | final class ItemCreationView: UIView { 4 | 5 | let nameTextField: UITextField = { 6 | let textField = UITextField() 7 | textField.placeholder = "Enter item name" 8 | return textField 9 | }() 10 | 11 | let createButton: UIButton = { 12 | let button = UIButton(type: .system) 13 | button.setTitle("Create item", for: .normal) 14 | return button 15 | }() 16 | 17 | private let scrollView: UIScrollView = { 18 | let scrollView = UIScrollView() 19 | return scrollView 20 | }() 21 | 22 | override init(frame: CGRect) { 23 | super.init(frame: frame) 24 | 25 | backgroundColor = .white 26 | 27 | addSubview(scrollView) 28 | scrollView.addSubview(nameTextField) 29 | scrollView.addSubview(createButton) 30 | } 31 | 32 | required init?(coder aDecoder: NSCoder) { 33 | fatalError("init(coder:) has not been implemented") 34 | } 35 | 36 | override func layoutSubviews() { 37 | super.layoutSubviews() 38 | 39 | let offset: CGFloat = 15 40 | 41 | scrollView.frame = bounds 42 | 43 | nameTextField.frame = CGRect( 44 | x: offset, 45 | y: offset, 46 | width: bounds.width - offset * 2, 47 | height: 44 48 | ) 49 | 50 | createButton.frame = CGRect( 51 | x: offset, 52 | y: nameTextField.frame.maxY + offset, 53 | width: bounds.width - offset * 2, 54 | height: 44 55 | ) 56 | 57 | scrollView.contentSize = CGSize(width: bounds.width, height: createButton.frame.maxY) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /CoordinatorExample/Supporting Files/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 | -------------------------------------------------------------------------------- /CoordinatorExample/Resources/Images/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 | "info" : { 90 | "version" : 1, 91 | "author" : "xcode" 92 | } 93 | } -------------------------------------------------------------------------------- /CoordinatorExample/Classes/Presentation Layer/Flows/MainFlow/MainCoordinator.swift: -------------------------------------------------------------------------------- 1 | protocol MainCoordinator: Coordinator { 2 | 3 | typealias LogoutBlock = () -> Void 4 | 5 | var onLogout: LogoutBlock? { get set } 6 | } 7 | 8 | final class MainCoordinatorImpl: BaseCoordinator, MainCoordinator { 9 | 10 | var onLogout: LogoutBlock? 11 | 12 | private var deepLink: DeepLinkOption? 13 | 14 | override func start(with option: DeepLinkOption?) { 15 | showItemsList() 16 | 17 | deepLink = option 18 | } 19 | 20 | private func showItemsList() { 21 | var module = assembler.resolver.resolve(ItemsListModule.self) 22 | module?.onViewDidAppear = { [weak self] in 23 | guard let deepLink = self?.deepLink as? CEDeepLinkOption else { return } 24 | switch deepLink { 25 | case .itemCreation: 26 | self?.runItemCreationFlow(animated: true) 27 | } 28 | self?.deepLink = nil 29 | } 30 | module?.onItemCreate = { [weak self] in 31 | self?.runItemCreationFlow(animated: true) 32 | } 33 | module?.onItemSelect = showDetails 34 | module?.onLogout = onLogout 35 | router.push(module, animated: false) 36 | } 37 | 38 | private func showDetails(for item: Item) { 39 | let input: ItemDetailsModule.Input = item 40 | let module = assembler.resolver.resolve(ItemDetailsModule.self, argument: input) 41 | router.push(module) 42 | } 43 | 44 | private func runItemCreationFlow(animated: Bool) { 45 | let coordinator = assembler.resolver.resolve(ItemCreationCoordinator.self, argument: assembler)! 46 | coordinator.onFinish = { [weak coordinator, weak self] in 47 | self?.removeDependency(coordinator) 48 | self?.router.dismissModule() 49 | } 50 | addDependency(coordinator) 51 | router.present(coordinator.router, animated: animated) 52 | coordinator.start() 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /CoordinatorExample/Classes/Presentation Layer/Flows/Routers/AppRouter.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | final class AppRouter: Routable { 4 | 5 | // MARK: Presentable 6 | 7 | var toPresent: UIViewController? { 8 | return rootController 9 | } 10 | 11 | private var rootController: UIViewController? { 12 | get { 13 | return window?.rootViewController 14 | } set { 15 | window?.rootViewController = newValue 16 | } 17 | } 18 | private weak var window: UIWindow? 19 | 20 | init(window: UIWindow?) { 21 | self.window = window 22 | } 23 | 24 | func setRootModule(_ module: Presentable?, animated: Bool) { 25 | guard let toController = module?.toPresent else { return } 26 | if let fromController = rootController, animated { 27 | let snapshot = fromController.view.snapshotView(afterScreenUpdates: true)! 28 | toController.view.addSubview(snapshot) 29 | rootController = toController 30 | UIView.animate(withDuration: 0.3, animations: { 31 | snapshot.alpha = 0.0 32 | snapshot.layer.transform = CATransform3DMakeScale(1.5, 1.5, 1.5) 33 | }, completion: { _ in 34 | snapshot.removeFromSuperview() 35 | }) 36 | } else { 37 | rootController = toController 38 | } 39 | } 40 | 41 | func present(_ module: Presentable?, animated: Bool) { 42 | guard let controllerToPresent = module?.toPresent else { return } 43 | rootController?.present(controllerToPresent, animated: animated, completion: nil) 44 | } 45 | 46 | func dismissModule(animated: Bool, completion: (() -> Void)?) { 47 | rootController?.dismiss(animated: animated, completion: nil) 48 | } 49 | 50 | func push(_ module: Presentable?, animated: Bool) { 51 | guard let controllerToPush = module?.toPresent else { return } 52 | (rootController as? UINavigationController)?.pushViewController(controllerToPush, animated: animated) 53 | } 54 | 55 | func popModule(animated: Bool) { 56 | (rootController as? UINavigationController)?.popViewController(animated: animated) 57 | } 58 | } 59 | 60 | -------------------------------------------------------------------------------- /CoordinatorExample/Classes/Presentation Layer/Flows/MainFlow/ItemsListModule/ViewController/ItemsListViewController.swift: -------------------------------------------------------------------------------- 1 | 2 | import UIKit 3 | 4 | final class ItemsListViewController: UIViewController, 5 | ItemsListModule, 6 | ViewHolder, 7 | UITableViewDelegate, 8 | UITableViewDataSource { 9 | 10 | var onViewDidAppear: ViewDidAppearBlock? 11 | var onItemCreate: ItemCreationBlock? 12 | var onItemSelect: ItemSelectBlock? 13 | var onLogout: LogoutBlock? 14 | 15 | var viewModel: ItemsListViewModel! 16 | 17 | // MARK: View lifecycle 18 | 19 | typealias RootViewType = ItemsListView 20 | 21 | override func loadView() { 22 | view = ItemsListView() 23 | } 24 | 25 | override func viewDidLoad() { 26 | super.viewDidLoad() 27 | 28 | title = "Items" 29 | navigationItem.rightBarButtonItem = UIBarButtonItem( 30 | barButtonSystemItem: .add, 31 | target: self, 32 | action: #selector(handleAddButtonTap) 33 | ) 34 | navigationItem.leftBarButtonItem = UIBarButtonItem( 35 | title: "Logout", 36 | style: .plain, 37 | target: self, 38 | action: #selector(handleLogoutButtonTap) 39 | ) 40 | 41 | bindTableView() 42 | bindItemsUpdate() 43 | 44 | viewModel.startUpdatingItems() 45 | rootView.itemsTableView.reloadData() 46 | } 47 | 48 | override func viewDidAppear(_ animated: Bool) { 49 | super.viewDidAppear(animated) 50 | 51 | onViewDidAppear?() 52 | } 53 | 54 | // MARK: Bindings 55 | 56 | private func bindTableView() { 57 | rootView.itemsTableView.dataSource = self 58 | rootView.itemsTableView.delegate = self 59 | 60 | rootView.itemsTableView.register( 61 | UITableViewCell.self, 62 | forCellReuseIdentifier: Constants.itemCellKey 63 | ) 64 | } 65 | 66 | private func bindItemsUpdate() { 67 | viewModel.onItemsUpdate = { [weak tableView = rootView.itemsTableView] in 68 | tableView?.reloadData() 69 | } 70 | } 71 | 72 | @objc 73 | private func handleAddButtonTap() { 74 | onItemCreate?() 75 | } 76 | 77 | @objc 78 | private func handleLogoutButtonTap() { 79 | viewModel.logout() 80 | onLogout?() 81 | } 82 | 83 | // MARK: UITableViewDataSource 84 | 85 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 86 | return viewModel.items.count 87 | } 88 | 89 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 90 | let cell = tableView.dequeueReusableCell( 91 | withIdentifier: Constants.itemCellKey, 92 | for: indexPath 93 | ) 94 | let item = viewModel.items[indexPath.row] 95 | cell.textLabel?.text = item.name 96 | return cell 97 | } 98 | 99 | // MARK: UITableViewDelegate 100 | 101 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 102 | tableView.deselectRow(at: indexPath, animated: true) 103 | 104 | onItemSelect?(viewModel.items[indexPath.row]) 105 | } 106 | 107 | deinit { 108 | viewModel.stopUpdatingItems() 109 | } 110 | } 111 | 112 | private extension ItemsListViewController { 113 | 114 | struct Constants { 115 | 116 | static let itemCellKey = "item_cell_key" 117 | 118 | private init() { } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /CoordinatorExample.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 48; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 0C506E582097014500E67227 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C506E572097014500E67227 /* AppDelegate.swift */; }; 11 | 0C506E5F2097014500E67227 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0C506E5E2097014500E67227 /* Assets.xcassets */; }; 12 | 0C506E622097014500E67227 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0C506E602097014500E67227 /* LaunchScreen.storyboard */; }; 13 | 0C506E722097027400E67227 /* Coordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C506E712097027400E67227 /* Coordinator.swift */; }; 14 | 0C506E742097029900E67227 /* DeepLinkOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C506E732097029900E67227 /* DeepLinkOption.swift */; }; 15 | 0C506E76209702F400E67227 /* Presentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C506E75209702F300E67227 /* Presentable.swift */; }; 16 | 0C506E782097035D00E67227 /* Routable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C506E772097035D00E67227 /* Routable.swift */; }; 17 | 0C506E7A2097039900E67227 /* BaseCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C506E792097039900E67227 /* BaseCoordinator.swift */; }; 18 | 0C506E7E2097045900E67227 /* NavigationRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C506E7D2097045900E67227 /* NavigationRouter.swift */; }; 19 | 0CAABB322098F13C001E25B6 /* AppCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CAABB312098F13C001E25B6 /* AppCoordinator.swift */; }; 20 | 0CAABB382098F1C3001E25B6 /* AuthService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CAABB372098F1C3001E25B6 /* AuthService.swift */; }; 21 | 0CAABB3D2098F349001E25B6 /* AppCoordinatorAssembly.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CAABB3C2098F349001E25B6 /* AppCoordinatorAssembly.swift */; }; 22 | 0CAABB3F2098F37A001E25B6 /* AppRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CAABB3E2098F37A001E25B6 /* AppRouter.swift */; }; 23 | 0CAABB412098F423001E25B6 /* MainCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CAABB402098F423001E25B6 /* MainCoordinator.swift */; }; 24 | 0CAABB432098F42A001E25B6 /* AuthCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CAABB422098F42A001E25B6 /* AuthCoordinator.swift */; }; 25 | 0CAABB452098F432001E25B6 /* MainCoordinatorAssembly.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CAABB442098F432001E25B6 /* MainCoordinatorAssembly.swift */; }; 26 | 0CAABB472098F43E001E25B6 /* AuthCoordinatorAssembly.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CAABB462098F43E001E25B6 /* AuthCoordinatorAssembly.swift */; }; 27 | 0CAABB492098F44D001E25B6 /* ServicesAssembly.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CAABB482098F44D001E25B6 /* ServicesAssembly.swift */; }; 28 | 0CAABB4C2098F4A2001E25B6 /* AuthStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CAABB4B2098F4A2001E25B6 /* AuthStorage.swift */; }; 29 | 0CAABB4E2098F88E001E25B6 /* AppDelegate+Coordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CAABB4D2098F88E001E25B6 /* AppDelegate+Coordinator.swift */; }; 30 | 0CAABB512098FAD4001E25B6 /* CEDeepLinkOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CAABB502098FAD4001E25B6 /* CEDeepLinkOption.swift */; }; 31 | 0CAABB542098FCD1001E25B6 /* AuthModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CAABB532098FCD1001E25B6 /* AuthModule.swift */; }; 32 | 0CAABB592098FCEC001E25B6 /* AuthView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CAABB582098FCEC001E25B6 /* AuthView.swift */; }; 33 | 0CAABB5B2098FD8C001E25B6 /* AuthViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CAABB5A2098FD8C001E25B6 /* AuthViewController.swift */; }; 34 | 0CAABB5F2098FDFA001E25B6 /* ViewHolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CAABB5E2098FDFA001E25B6 /* ViewHolder.swift */; }; 35 | 0CAABB612098FE36001E25B6 /* AuthViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CAABB602098FE36001E25B6 /* AuthViewModel.swift */; }; 36 | 0CAABB64209901B3001E25B6 /* AuthModuleAssembly.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CAABB63209901B3001E25B6 /* AuthModuleAssembly.swift */; }; 37 | 0CAABB672099DA6C001E25B6 /* ItemsListModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CAABB662099DA6C001E25B6 /* ItemsListModule.swift */; }; 38 | 0CAABB6C2099DA94001E25B6 /* ItemsListModuleAssembly.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CAABB6B2099DA94001E25B6 /* ItemsListModuleAssembly.swift */; }; 39 | 0CAABB6E2099DAC7001E25B6 /* ItemsListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CAABB6D2099DAC7001E25B6 /* ItemsListView.swift */; }; 40 | 0CAABB702099DB3E001E25B6 /* ItemsListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CAABB6F2099DB3E001E25B6 /* ItemsListViewModel.swift */; }; 41 | 0CAABB722099DB48001E25B6 /* ItemsListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CAABB712099DB48001E25B6 /* ItemsListViewController.swift */; }; 42 | 0CAABB752099DB71001E25B6 /* Item.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CAABB742099DB71001E25B6 /* Item.swift */; }; 43 | 0CAABB772099DC10001E25B6 /* ItemsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CAABB762099DC10001E25B6 /* ItemsService.swift */; }; 44 | 0CAABB7A2099E351001E25B6 /* ItemCreationModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CAABB792099E351001E25B6 /* ItemCreationModule.swift */; }; 45 | 0CAABB7D2099E36E001E25B6 /* ItemCreationModuleAssembly.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CAABB7C2099E36E001E25B6 /* ItemCreationModuleAssembly.swift */; }; 46 | 0CAABB822099E389001E25B6 /* ItemCreationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CAABB812099E389001E25B6 /* ItemCreationView.swift */; }; 47 | 0CAABB842099E390001E25B6 /* ItemCreationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CAABB832099E390001E25B6 /* ItemCreationViewController.swift */; }; 48 | 0CAABB862099E398001E25B6 /* ItemCreationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CAABB852099E398001E25B6 /* ItemCreationViewModel.swift */; }; 49 | 0CAABB882099EDB3001E25B6 /* ItemCreationCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CAABB872099EDB3001E25B6 /* ItemCreationCoordinator.swift */; }; 50 | 0CAABB8A2099EDC3001E25B6 /* ItemCreationCoordinatorAssembly.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CAABB892099EDC3001E25B6 /* ItemCreationCoordinatorAssembly.swift */; }; 51 | 0CAABB8C2099FD72001E25B6 /* Presentable+Navigation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CAABB8B2099FD72001E25B6 /* Presentable+Navigation.swift */; }; 52 | 0CAABB8F209B8B79001E25B6 /* ItemDetailsModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CAABB8E209B8B79001E25B6 /* ItemDetailsModule.swift */; }; 53 | 0CAABB91209B8BA2001E25B6 /* ItemDetailsModuleAssembly.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CAABB90209B8BA2001E25B6 /* ItemDetailsModuleAssembly.swift */; }; 54 | 0CAABB96209B8C21001E25B6 /* ItemDetailsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CAABB95209B8C21001E25B6 /* ItemDetailsView.swift */; }; 55 | 0CAABB98209B8C29001E25B6 /* ItemDetailsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CAABB97209B8C29001E25B6 /* ItemDetailsViewController.swift */; }; 56 | 0CAABB9A209B8C39001E25B6 /* ItemDetailsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CAABB99209B8C39001E25B6 /* ItemDetailsViewModel.swift */; }; 57 | 5F6219355C3F90D3E26A817B /* libPods-CoordinatorExample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8D90A89E1B11200ACDF6DB9F /* libPods-CoordinatorExample.a */; }; 58 | /* End PBXBuildFile section */ 59 | 60 | /* Begin PBXFileReference section */ 61 | 0C506E542097014500E67227 /* CoordinatorExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CoordinatorExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 62 | 0C506E572097014500E67227 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 63 | 0C506E5E2097014500E67227 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 64 | 0C506E612097014500E67227 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 65 | 0C506E632097014500E67227 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 66 | 0C506E712097027400E67227 /* Coordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Coordinator.swift; sourceTree = ""; }; 67 | 0C506E732097029900E67227 /* DeepLinkOption.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeepLinkOption.swift; sourceTree = ""; }; 68 | 0C506E75209702F300E67227 /* Presentable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Presentable.swift; sourceTree = ""; }; 69 | 0C506E772097035D00E67227 /* Routable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Routable.swift; sourceTree = ""; }; 70 | 0C506E792097039900E67227 /* BaseCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseCoordinator.swift; sourceTree = ""; }; 71 | 0C506E7D2097045900E67227 /* NavigationRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationRouter.swift; sourceTree = ""; }; 72 | 0CAABB312098F13C001E25B6 /* AppCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppCoordinator.swift; sourceTree = ""; }; 73 | 0CAABB372098F1C3001E25B6 /* AuthService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthService.swift; sourceTree = ""; }; 74 | 0CAABB3C2098F349001E25B6 /* AppCoordinatorAssembly.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppCoordinatorAssembly.swift; sourceTree = ""; }; 75 | 0CAABB3E2098F37A001E25B6 /* AppRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppRouter.swift; sourceTree = ""; }; 76 | 0CAABB402098F423001E25B6 /* MainCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainCoordinator.swift; sourceTree = ""; }; 77 | 0CAABB422098F42A001E25B6 /* AuthCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthCoordinator.swift; sourceTree = ""; }; 78 | 0CAABB442098F432001E25B6 /* MainCoordinatorAssembly.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainCoordinatorAssembly.swift; sourceTree = ""; }; 79 | 0CAABB462098F43E001E25B6 /* AuthCoordinatorAssembly.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthCoordinatorAssembly.swift; sourceTree = ""; }; 80 | 0CAABB482098F44D001E25B6 /* ServicesAssembly.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServicesAssembly.swift; sourceTree = ""; }; 81 | 0CAABB4B2098F4A2001E25B6 /* AuthStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthStorage.swift; sourceTree = ""; }; 82 | 0CAABB4D2098F88E001E25B6 /* AppDelegate+Coordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+Coordinator.swift"; sourceTree = ""; }; 83 | 0CAABB502098FAD4001E25B6 /* CEDeepLinkOption.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CEDeepLinkOption.swift; sourceTree = ""; }; 84 | 0CAABB532098FCD1001E25B6 /* AuthModule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthModule.swift; sourceTree = ""; }; 85 | 0CAABB582098FCEC001E25B6 /* AuthView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthView.swift; sourceTree = ""; }; 86 | 0CAABB5A2098FD8C001E25B6 /* AuthViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthViewController.swift; sourceTree = ""; }; 87 | 0CAABB5E2098FDFA001E25B6 /* ViewHolder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewHolder.swift; sourceTree = ""; }; 88 | 0CAABB602098FE36001E25B6 /* AuthViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthViewModel.swift; sourceTree = ""; }; 89 | 0CAABB63209901B3001E25B6 /* AuthModuleAssembly.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthModuleAssembly.swift; sourceTree = ""; }; 90 | 0CAABB662099DA6C001E25B6 /* ItemsListModule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemsListModule.swift; sourceTree = ""; }; 91 | 0CAABB6B2099DA94001E25B6 /* ItemsListModuleAssembly.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemsListModuleAssembly.swift; sourceTree = ""; }; 92 | 0CAABB6D2099DAC7001E25B6 /* ItemsListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemsListView.swift; sourceTree = ""; }; 93 | 0CAABB6F2099DB3E001E25B6 /* ItemsListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemsListViewModel.swift; sourceTree = ""; }; 94 | 0CAABB712099DB48001E25B6 /* ItemsListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemsListViewController.swift; sourceTree = ""; }; 95 | 0CAABB742099DB71001E25B6 /* Item.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Item.swift; sourceTree = ""; }; 96 | 0CAABB762099DC10001E25B6 /* ItemsService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemsService.swift; sourceTree = ""; }; 97 | 0CAABB792099E351001E25B6 /* ItemCreationModule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemCreationModule.swift; sourceTree = ""; }; 98 | 0CAABB7C2099E36E001E25B6 /* ItemCreationModuleAssembly.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemCreationModuleAssembly.swift; sourceTree = ""; }; 99 | 0CAABB812099E389001E25B6 /* ItemCreationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemCreationView.swift; sourceTree = ""; }; 100 | 0CAABB832099E390001E25B6 /* ItemCreationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemCreationViewController.swift; sourceTree = ""; }; 101 | 0CAABB852099E398001E25B6 /* ItemCreationViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemCreationViewModel.swift; sourceTree = ""; }; 102 | 0CAABB872099EDB3001E25B6 /* ItemCreationCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemCreationCoordinator.swift; sourceTree = ""; }; 103 | 0CAABB892099EDC3001E25B6 /* ItemCreationCoordinatorAssembly.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemCreationCoordinatorAssembly.swift; sourceTree = ""; }; 104 | 0CAABB8B2099FD72001E25B6 /* Presentable+Navigation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Presentable+Navigation.swift"; sourceTree = ""; }; 105 | 0CAABB8E209B8B79001E25B6 /* ItemDetailsModule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemDetailsModule.swift; sourceTree = ""; }; 106 | 0CAABB90209B8BA2001E25B6 /* ItemDetailsModuleAssembly.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemDetailsModuleAssembly.swift; sourceTree = ""; }; 107 | 0CAABB95209B8C21001E25B6 /* ItemDetailsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemDetailsView.swift; sourceTree = ""; }; 108 | 0CAABB97209B8C29001E25B6 /* ItemDetailsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemDetailsViewController.swift; sourceTree = ""; }; 109 | 0CAABB99209B8C39001E25B6 /* ItemDetailsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemDetailsViewModel.swift; sourceTree = ""; }; 110 | 8D90A89E1B11200ACDF6DB9F /* libPods-CoordinatorExample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-CoordinatorExample.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 111 | A4322504D4287A54A061F9DB /* Pods-CoordinatorExample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CoordinatorExample.release.xcconfig"; path = "Pods/Target Support Files/Pods-CoordinatorExample/Pods-CoordinatorExample.release.xcconfig"; sourceTree = ""; }; 112 | B391B5DD98361F650B96D676 /* Pods-CoordinatorExample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CoordinatorExample.debug.xcconfig"; path = "Pods/Target Support Files/Pods-CoordinatorExample/Pods-CoordinatorExample.debug.xcconfig"; sourceTree = ""; }; 113 | /* End PBXFileReference section */ 114 | 115 | /* Begin PBXFrameworksBuildPhase section */ 116 | 0C506E512097014500E67227 /* Frameworks */ = { 117 | isa = PBXFrameworksBuildPhase; 118 | buildActionMask = 2147483647; 119 | files = ( 120 | 5F6219355C3F90D3E26A817B /* libPods-CoordinatorExample.a in Frameworks */, 121 | ); 122 | runOnlyForDeploymentPostprocessing = 0; 123 | }; 124 | /* End PBXFrameworksBuildPhase section */ 125 | 126 | /* Begin PBXGroup section */ 127 | 0C506E4B2097014500E67227 = { 128 | isa = PBXGroup; 129 | children = ( 130 | 0C506E562097014500E67227 /* CoordinatorExample */, 131 | 0C506E552097014500E67227 /* Products */, 132 | 82A95A8EBEB545867EC81C11 /* Pods */, 133 | 52BD9685851D9FCE276BE018 /* Frameworks */, 134 | ); 135 | sourceTree = ""; 136 | }; 137 | 0C506E552097014500E67227 /* Products */ = { 138 | isa = PBXGroup; 139 | children = ( 140 | 0C506E542097014500E67227 /* CoordinatorExample.app */, 141 | ); 142 | name = Products; 143 | sourceTree = ""; 144 | }; 145 | 0C506E562097014500E67227 /* CoordinatorExample */ = { 146 | isa = PBXGroup; 147 | children = ( 148 | 0C506E6E209701B700E67227 /* Classes */, 149 | 0C506E692097015C00E67227 /* Resources */, 150 | 0C506E6B2097017100E67227 /* Supporting Files */, 151 | ); 152 | path = CoordinatorExample; 153 | sourceTree = ""; 154 | }; 155 | 0C506E692097015C00E67227 /* Resources */ = { 156 | isa = PBXGroup; 157 | children = ( 158 | 0C506E6A2097016300E67227 /* Images */, 159 | ); 160 | path = Resources; 161 | sourceTree = ""; 162 | }; 163 | 0C506E6A2097016300E67227 /* Images */ = { 164 | isa = PBXGroup; 165 | children = ( 166 | 0C506E5E2097014500E67227 /* Assets.xcassets */, 167 | ); 168 | path = Images; 169 | sourceTree = ""; 170 | }; 171 | 0C506E6B2097017100E67227 /* Supporting Files */ = { 172 | isa = PBXGroup; 173 | children = ( 174 | 0C506E602097014500E67227 /* LaunchScreen.storyboard */, 175 | 0C506E632097014500E67227 /* Info.plist */, 176 | ); 177 | path = "Supporting Files"; 178 | sourceTree = ""; 179 | }; 180 | 0C506E6C209701A000E67227 /* Presentation Layer */ = { 181 | isa = PBXGroup; 182 | children = ( 183 | 0CAABB392098F32C001E25B6 /* Flows */, 184 | 0C506E702097022B00E67227 /* Coordinator */, 185 | 0CAABB5C2098FDCE001E25B6 /* Helpers */, 186 | ); 187 | path = "Presentation Layer"; 188 | sourceTree = ""; 189 | }; 190 | 0C506E6D209701A500E67227 /* Application Layer */ = { 191 | isa = PBXGroup; 192 | children = ( 193 | 0C506E572097014500E67227 /* AppDelegate.swift */, 194 | 0CAABB482098F44D001E25B6 /* ServicesAssembly.swift */, 195 | 0CAABB4D2098F88E001E25B6 /* AppDelegate+Coordinator.swift */, 196 | 0CAABB502098FAD4001E25B6 /* CEDeepLinkOption.swift */, 197 | ); 198 | path = "Application Layer"; 199 | sourceTree = ""; 200 | }; 201 | 0C506E6E209701B700E67227 /* Classes */ = { 202 | isa = PBXGroup; 203 | children = ( 204 | 0C506E6F209701CA00E67227 /* Model */, 205 | 0C506E6D209701A500E67227 /* Application Layer */, 206 | 0C506E6C209701A000E67227 /* Presentation Layer */, 207 | ); 208 | path = Classes; 209 | sourceTree = ""; 210 | }; 211 | 0C506E6F209701CA00E67227 /* Model */ = { 212 | isa = PBXGroup; 213 | children = ( 214 | 0CAABB732099DB63001E25B6 /* Entities */, 215 | 0CAABB4A2098F493001E25B6 /* Storage */, 216 | 0CAABB362098F1B9001E25B6 /* Services */, 217 | ); 218 | path = Model; 219 | sourceTree = ""; 220 | }; 221 | 0C506E702097022B00E67227 /* Coordinator */ = { 222 | isa = PBXGroup; 223 | children = ( 224 | 0C506E7B209703A300E67227 /* Protocols */, 225 | 0C506E7C209703A800E67227 /* Implementations */, 226 | ); 227 | path = Coordinator; 228 | sourceTree = ""; 229 | }; 230 | 0C506E7B209703A300E67227 /* Protocols */ = { 231 | isa = PBXGroup; 232 | children = ( 233 | 0C506E712097027400E67227 /* Coordinator.swift */, 234 | 0C506E732097029900E67227 /* DeepLinkOption.swift */, 235 | 0C506E75209702F300E67227 /* Presentable.swift */, 236 | 0C506E772097035D00E67227 /* Routable.swift */, 237 | ); 238 | path = Protocols; 239 | sourceTree = ""; 240 | }; 241 | 0C506E7C209703A800E67227 /* Implementations */ = { 242 | isa = PBXGroup; 243 | children = ( 244 | 0C506E792097039900E67227 /* BaseCoordinator.swift */, 245 | 0CAABB8B2099FD72001E25B6 /* Presentable+Navigation.swift */, 246 | ); 247 | path = Implementations; 248 | sourceTree = ""; 249 | }; 250 | 0CAABB352098F1A8001E25B6 /* AppFlow */ = { 251 | isa = PBXGroup; 252 | children = ( 253 | 0CAABB312098F13C001E25B6 /* AppCoordinator.swift */, 254 | 0CAABB3C2098F349001E25B6 /* AppCoordinatorAssembly.swift */, 255 | ); 256 | path = AppFlow; 257 | sourceTree = ""; 258 | }; 259 | 0CAABB362098F1B9001E25B6 /* Services */ = { 260 | isa = PBXGroup; 261 | children = ( 262 | 0CAABB372098F1C3001E25B6 /* AuthService.swift */, 263 | 0CAABB762099DC10001E25B6 /* ItemsService.swift */, 264 | ); 265 | path = Services; 266 | sourceTree = ""; 267 | }; 268 | 0CAABB392098F32C001E25B6 /* Flows */ = { 269 | isa = PBXGroup; 270 | children = ( 271 | 0CAABB4F2098F967001E25B6 /* Routers */, 272 | 0CAABB352098F1A8001E25B6 /* AppFlow */, 273 | 0CAABB3A2098F332001E25B6 /* AuthFlow */, 274 | 0CAABB3B2098F335001E25B6 /* MainFlow */, 275 | ); 276 | path = Flows; 277 | sourceTree = ""; 278 | }; 279 | 0CAABB3A2098F332001E25B6 /* AuthFlow */ = { 280 | isa = PBXGroup; 281 | children = ( 282 | 0CAABB522098FCC7001E25B6 /* AuthModule */, 283 | 0CAABB422098F42A001E25B6 /* AuthCoordinator.swift */, 284 | 0CAABB462098F43E001E25B6 /* AuthCoordinatorAssembly.swift */, 285 | ); 286 | path = AuthFlow; 287 | sourceTree = ""; 288 | }; 289 | 0CAABB3B2098F335001E25B6 /* MainFlow */ = { 290 | isa = PBXGroup; 291 | children = ( 292 | 0CAABB7B2099E353001E25B6 /* ItemCreationFlow */, 293 | 0CAABB652099DA60001E25B6 /* ItemsListModule */, 294 | 0CAABB8D209B8B6A001E25B6 /* ItemDetailsModule */, 295 | 0CAABB402098F423001E25B6 /* MainCoordinator.swift */, 296 | 0CAABB442098F432001E25B6 /* MainCoordinatorAssembly.swift */, 297 | ); 298 | path = MainFlow; 299 | sourceTree = ""; 300 | }; 301 | 0CAABB4A2098F493001E25B6 /* Storage */ = { 302 | isa = PBXGroup; 303 | children = ( 304 | 0CAABB4B2098F4A2001E25B6 /* AuthStorage.swift */, 305 | ); 306 | path = Storage; 307 | sourceTree = ""; 308 | }; 309 | 0CAABB4F2098F967001E25B6 /* Routers */ = { 310 | isa = PBXGroup; 311 | children = ( 312 | 0CAABB3E2098F37A001E25B6 /* AppRouter.swift */, 313 | 0C506E7D2097045900E67227 /* NavigationRouter.swift */, 314 | ); 315 | path = Routers; 316 | sourceTree = ""; 317 | }; 318 | 0CAABB522098FCC7001E25B6 /* AuthModule */ = { 319 | isa = PBXGroup; 320 | children = ( 321 | 0CAABB572098FCE1001E25B6 /* ViewModel */, 322 | 0CAABB562098FCDD001E25B6 /* ViewController */, 323 | 0CAABB552098FCD3001E25B6 /* View */, 324 | 0CAABB532098FCD1001E25B6 /* AuthModule.swift */, 325 | 0CAABB63209901B3001E25B6 /* AuthModuleAssembly.swift */, 326 | ); 327 | path = AuthModule; 328 | sourceTree = ""; 329 | }; 330 | 0CAABB552098FCD3001E25B6 /* View */ = { 331 | isa = PBXGroup; 332 | children = ( 333 | 0CAABB582098FCEC001E25B6 /* AuthView.swift */, 334 | ); 335 | path = View; 336 | sourceTree = ""; 337 | }; 338 | 0CAABB562098FCDD001E25B6 /* ViewController */ = { 339 | isa = PBXGroup; 340 | children = ( 341 | 0CAABB5A2098FD8C001E25B6 /* AuthViewController.swift */, 342 | ); 343 | path = ViewController; 344 | sourceTree = ""; 345 | }; 346 | 0CAABB572098FCE1001E25B6 /* ViewModel */ = { 347 | isa = PBXGroup; 348 | children = ( 349 | 0CAABB602098FE36001E25B6 /* AuthViewModel.swift */, 350 | ); 351 | path = ViewModel; 352 | sourceTree = ""; 353 | }; 354 | 0CAABB5C2098FDCE001E25B6 /* Helpers */ = { 355 | isa = PBXGroup; 356 | children = ( 357 | 0CAABB5D2098FDE8001E25B6 /* ViewHolder */, 358 | ); 359 | path = Helpers; 360 | sourceTree = ""; 361 | }; 362 | 0CAABB5D2098FDE8001E25B6 /* ViewHolder */ = { 363 | isa = PBXGroup; 364 | children = ( 365 | 0CAABB5E2098FDFA001E25B6 /* ViewHolder.swift */, 366 | ); 367 | path = ViewHolder; 368 | sourceTree = ""; 369 | }; 370 | 0CAABB652099DA60001E25B6 /* ItemsListModule */ = { 371 | isa = PBXGroup; 372 | children = ( 373 | 0CAABB6A2099DA86001E25B6 /* ViewModel */, 374 | 0CAABB692099DA80001E25B6 /* ViewController */, 375 | 0CAABB682099DA6F001E25B6 /* View */, 376 | 0CAABB662099DA6C001E25B6 /* ItemsListModule.swift */, 377 | 0CAABB6B2099DA94001E25B6 /* ItemsListModuleAssembly.swift */, 378 | ); 379 | path = ItemsListModule; 380 | sourceTree = ""; 381 | }; 382 | 0CAABB682099DA6F001E25B6 /* View */ = { 383 | isa = PBXGroup; 384 | children = ( 385 | 0CAABB6D2099DAC7001E25B6 /* ItemsListView.swift */, 386 | ); 387 | path = View; 388 | sourceTree = ""; 389 | }; 390 | 0CAABB692099DA80001E25B6 /* ViewController */ = { 391 | isa = PBXGroup; 392 | children = ( 393 | 0CAABB712099DB48001E25B6 /* ItemsListViewController.swift */, 394 | ); 395 | path = ViewController; 396 | sourceTree = ""; 397 | }; 398 | 0CAABB6A2099DA86001E25B6 /* ViewModel */ = { 399 | isa = PBXGroup; 400 | children = ( 401 | 0CAABB6F2099DB3E001E25B6 /* ItemsListViewModel.swift */, 402 | ); 403 | path = ViewModel; 404 | sourceTree = ""; 405 | }; 406 | 0CAABB732099DB63001E25B6 /* Entities */ = { 407 | isa = PBXGroup; 408 | children = ( 409 | 0CAABB742099DB71001E25B6 /* Item.swift */, 410 | ); 411 | path = Entities; 412 | sourceTree = ""; 413 | }; 414 | 0CAABB782099E18C001E25B6 /* ItemCreationModule */ = { 415 | isa = PBXGroup; 416 | children = ( 417 | 0CAABB7F2099E375001E25B6 /* ViewModel */, 418 | 0CAABB802099E379001E25B6 /* ViewController */, 419 | 0CAABB7E2099E370001E25B6 /* View */, 420 | 0CAABB792099E351001E25B6 /* ItemCreationModule.swift */, 421 | 0CAABB7C2099E36E001E25B6 /* ItemCreationModuleAssembly.swift */, 422 | ); 423 | path = ItemCreationModule; 424 | sourceTree = ""; 425 | }; 426 | 0CAABB7B2099E353001E25B6 /* ItemCreationFlow */ = { 427 | isa = PBXGroup; 428 | children = ( 429 | 0CAABB782099E18C001E25B6 /* ItemCreationModule */, 430 | 0CAABB872099EDB3001E25B6 /* ItemCreationCoordinator.swift */, 431 | 0CAABB892099EDC3001E25B6 /* ItemCreationCoordinatorAssembly.swift */, 432 | ); 433 | path = ItemCreationFlow; 434 | sourceTree = ""; 435 | }; 436 | 0CAABB7E2099E370001E25B6 /* View */ = { 437 | isa = PBXGroup; 438 | children = ( 439 | 0CAABB812099E389001E25B6 /* ItemCreationView.swift */, 440 | ); 441 | path = View; 442 | sourceTree = ""; 443 | }; 444 | 0CAABB7F2099E375001E25B6 /* ViewModel */ = { 445 | isa = PBXGroup; 446 | children = ( 447 | 0CAABB852099E398001E25B6 /* ItemCreationViewModel.swift */, 448 | ); 449 | path = ViewModel; 450 | sourceTree = ""; 451 | }; 452 | 0CAABB802099E379001E25B6 /* ViewController */ = { 453 | isa = PBXGroup; 454 | children = ( 455 | 0CAABB832099E390001E25B6 /* ItemCreationViewController.swift */, 456 | ); 457 | path = ViewController; 458 | sourceTree = ""; 459 | }; 460 | 0CAABB8D209B8B6A001E25B6 /* ItemDetailsModule */ = { 461 | isa = PBXGroup; 462 | children = ( 463 | 0CAABB94209B8C13001E25B6 /* ViewModel */, 464 | 0CAABB93209B8BDD001E25B6 /* ViewController */, 465 | 0CAABB92209B8BDA001E25B6 /* View */, 466 | 0CAABB8E209B8B79001E25B6 /* ItemDetailsModule.swift */, 467 | 0CAABB90209B8BA2001E25B6 /* ItemDetailsModuleAssembly.swift */, 468 | ); 469 | path = ItemDetailsModule; 470 | sourceTree = ""; 471 | }; 472 | 0CAABB92209B8BDA001E25B6 /* View */ = { 473 | isa = PBXGroup; 474 | children = ( 475 | 0CAABB95209B8C21001E25B6 /* ItemDetailsView.swift */, 476 | ); 477 | path = View; 478 | sourceTree = ""; 479 | }; 480 | 0CAABB93209B8BDD001E25B6 /* ViewController */ = { 481 | isa = PBXGroup; 482 | children = ( 483 | 0CAABB97209B8C29001E25B6 /* ItemDetailsViewController.swift */, 484 | ); 485 | path = ViewController; 486 | sourceTree = ""; 487 | }; 488 | 0CAABB94209B8C13001E25B6 /* ViewModel */ = { 489 | isa = PBXGroup; 490 | children = ( 491 | 0CAABB99209B8C39001E25B6 /* ItemDetailsViewModel.swift */, 492 | ); 493 | path = ViewModel; 494 | sourceTree = ""; 495 | }; 496 | 52BD9685851D9FCE276BE018 /* Frameworks */ = { 497 | isa = PBXGroup; 498 | children = ( 499 | 8D90A89E1B11200ACDF6DB9F /* libPods-CoordinatorExample.a */, 500 | ); 501 | name = Frameworks; 502 | sourceTree = ""; 503 | }; 504 | 82A95A8EBEB545867EC81C11 /* Pods */ = { 505 | isa = PBXGroup; 506 | children = ( 507 | B391B5DD98361F650B96D676 /* Pods-CoordinatorExample.debug.xcconfig */, 508 | A4322504D4287A54A061F9DB /* Pods-CoordinatorExample.release.xcconfig */, 509 | ); 510 | name = Pods; 511 | sourceTree = ""; 512 | }; 513 | /* End PBXGroup section */ 514 | 515 | /* Begin PBXNativeTarget section */ 516 | 0C506E532097014500E67227 /* CoordinatorExample */ = { 517 | isa = PBXNativeTarget; 518 | buildConfigurationList = 0C506E662097014500E67227 /* Build configuration list for PBXNativeTarget "CoordinatorExample" */; 519 | buildPhases = ( 520 | 494FBF751F6BBD64414558B0 /* [CP] Check Pods Manifest.lock */, 521 | 0C506E502097014500E67227 /* Sources */, 522 | 0C506E512097014500E67227 /* Frameworks */, 523 | 0C506E522097014500E67227 /* Resources */, 524 | ); 525 | buildRules = ( 526 | ); 527 | dependencies = ( 528 | ); 529 | name = CoordinatorExample; 530 | productName = CoordinatorExample; 531 | productReference = 0C506E542097014500E67227 /* CoordinatorExample.app */; 532 | productType = "com.apple.product-type.application"; 533 | }; 534 | /* End PBXNativeTarget section */ 535 | 536 | /* Begin PBXProject section */ 537 | 0C506E4C2097014500E67227 /* Project object */ = { 538 | isa = PBXProject; 539 | attributes = { 540 | LastSwiftUpdateCheck = 0920; 541 | LastUpgradeCheck = 0920; 542 | ORGANIZATIONNAME = "Andrei Rychkov"; 543 | TargetAttributes = { 544 | 0C506E532097014500E67227 = { 545 | CreatedOnToolsVersion = 9.2; 546 | ProvisioningStyle = Automatic; 547 | }; 548 | }; 549 | }; 550 | buildConfigurationList = 0C506E4F2097014500E67227 /* Build configuration list for PBXProject "CoordinatorExample" */; 551 | compatibilityVersion = "Xcode 8.0"; 552 | developmentRegion = en; 553 | hasScannedForEncodings = 0; 554 | knownRegions = ( 555 | en, 556 | Base, 557 | ); 558 | mainGroup = 0C506E4B2097014500E67227; 559 | productRefGroup = 0C506E552097014500E67227 /* Products */; 560 | projectDirPath = ""; 561 | projectRoot = ""; 562 | targets = ( 563 | 0C506E532097014500E67227 /* CoordinatorExample */, 564 | ); 565 | }; 566 | /* End PBXProject section */ 567 | 568 | /* Begin PBXResourcesBuildPhase section */ 569 | 0C506E522097014500E67227 /* Resources */ = { 570 | isa = PBXResourcesBuildPhase; 571 | buildActionMask = 2147483647; 572 | files = ( 573 | 0C506E622097014500E67227 /* LaunchScreen.storyboard in Resources */, 574 | 0C506E5F2097014500E67227 /* Assets.xcassets in Resources */, 575 | ); 576 | runOnlyForDeploymentPostprocessing = 0; 577 | }; 578 | /* End PBXResourcesBuildPhase section */ 579 | 580 | /* Begin PBXShellScriptBuildPhase section */ 581 | 494FBF751F6BBD64414558B0 /* [CP] Check Pods Manifest.lock */ = { 582 | isa = PBXShellScriptBuildPhase; 583 | buildActionMask = 2147483647; 584 | files = ( 585 | ); 586 | inputPaths = ( 587 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 588 | "${PODS_ROOT}/Manifest.lock", 589 | ); 590 | name = "[CP] Check Pods Manifest.lock"; 591 | outputPaths = ( 592 | "$(DERIVED_FILE_DIR)/Pods-CoordinatorExample-checkManifestLockResult.txt", 593 | ); 594 | runOnlyForDeploymentPostprocessing = 0; 595 | shellPath = /bin/sh; 596 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 597 | showEnvVarsInLog = 0; 598 | }; 599 | /* End PBXShellScriptBuildPhase section */ 600 | 601 | /* Begin PBXSourcesBuildPhase section */ 602 | 0C506E502097014500E67227 /* Sources */ = { 603 | isa = PBXSourcesBuildPhase; 604 | buildActionMask = 2147483647; 605 | files = ( 606 | 0CAABB772099DC10001E25B6 /* ItemsService.swift in Sources */, 607 | 0CAABB822099E389001E25B6 /* ItemCreationView.swift in Sources */, 608 | 0CAABB5B2098FD8C001E25B6 /* AuthViewController.swift in Sources */, 609 | 0C506E722097027400E67227 /* Coordinator.swift in Sources */, 610 | 0CAABB432098F42A001E25B6 /* AuthCoordinator.swift in Sources */, 611 | 0CAABB7D2099E36E001E25B6 /* ItemCreationModuleAssembly.swift in Sources */, 612 | 0CAABB4C2098F4A2001E25B6 /* AuthStorage.swift in Sources */, 613 | 0CAABB412098F423001E25B6 /* MainCoordinator.swift in Sources */, 614 | 0CAABB4E2098F88E001E25B6 /* AppDelegate+Coordinator.swift in Sources */, 615 | 0CAABB752099DB71001E25B6 /* Item.swift in Sources */, 616 | 0C506E7E2097045900E67227 /* NavigationRouter.swift in Sources */, 617 | 0C506E7A2097039900E67227 /* BaseCoordinator.swift in Sources */, 618 | 0CAABB492098F44D001E25B6 /* ServicesAssembly.swift in Sources */, 619 | 0CAABB452098F432001E25B6 /* MainCoordinatorAssembly.swift in Sources */, 620 | 0CAABB382098F1C3001E25B6 /* AuthService.swift in Sources */, 621 | 0CAABB702099DB3E001E25B6 /* ItemsListViewModel.swift in Sources */, 622 | 0CAABB512098FAD4001E25B6 /* CEDeepLinkOption.swift in Sources */, 623 | 0CAABB672099DA6C001E25B6 /* ItemsListModule.swift in Sources */, 624 | 0CAABB6C2099DA94001E25B6 /* ItemsListModuleAssembly.swift in Sources */, 625 | 0CAABB3D2098F349001E25B6 /* AppCoordinatorAssembly.swift in Sources */, 626 | 0C506E76209702F400E67227 /* Presentable.swift in Sources */, 627 | 0CAABB722099DB48001E25B6 /* ItemsListViewController.swift in Sources */, 628 | 0CAABB7A2099E351001E25B6 /* ItemCreationModule.swift in Sources */, 629 | 0CAABB322098F13C001E25B6 /* AppCoordinator.swift in Sources */, 630 | 0CAABB882099EDB3001E25B6 /* ItemCreationCoordinator.swift in Sources */, 631 | 0CAABB592098FCEC001E25B6 /* AuthView.swift in Sources */, 632 | 0CAABB3F2098F37A001E25B6 /* AppRouter.swift in Sources */, 633 | 0CAABB8F209B8B79001E25B6 /* ItemDetailsModule.swift in Sources */, 634 | 0CAABB5F2098FDFA001E25B6 /* ViewHolder.swift in Sources */, 635 | 0CAABB542098FCD1001E25B6 /* AuthModule.swift in Sources */, 636 | 0CAABB842099E390001E25B6 /* ItemCreationViewController.swift in Sources */, 637 | 0C506E742097029900E67227 /* DeepLinkOption.swift in Sources */, 638 | 0CAABB9A209B8C39001E25B6 /* ItemDetailsViewModel.swift in Sources */, 639 | 0CAABB96209B8C21001E25B6 /* ItemDetailsView.swift in Sources */, 640 | 0CAABB64209901B3001E25B6 /* AuthModuleAssembly.swift in Sources */, 641 | 0CAABB6E2099DAC7001E25B6 /* ItemsListView.swift in Sources */, 642 | 0CAABB612098FE36001E25B6 /* AuthViewModel.swift in Sources */, 643 | 0CAABB8A2099EDC3001E25B6 /* ItemCreationCoordinatorAssembly.swift in Sources */, 644 | 0C506E582097014500E67227 /* AppDelegate.swift in Sources */, 645 | 0CAABB98209B8C29001E25B6 /* ItemDetailsViewController.swift in Sources */, 646 | 0CAABB472098F43E001E25B6 /* AuthCoordinatorAssembly.swift in Sources */, 647 | 0C506E782097035D00E67227 /* Routable.swift in Sources */, 648 | 0CAABB91209B8BA2001E25B6 /* ItemDetailsModuleAssembly.swift in Sources */, 649 | 0CAABB862099E398001E25B6 /* ItemCreationViewModel.swift in Sources */, 650 | 0CAABB8C2099FD72001E25B6 /* Presentable+Navigation.swift in Sources */, 651 | ); 652 | runOnlyForDeploymentPostprocessing = 0; 653 | }; 654 | /* End PBXSourcesBuildPhase section */ 655 | 656 | /* Begin PBXVariantGroup section */ 657 | 0C506E602097014500E67227 /* LaunchScreen.storyboard */ = { 658 | isa = PBXVariantGroup; 659 | children = ( 660 | 0C506E612097014500E67227 /* Base */, 661 | ); 662 | name = LaunchScreen.storyboard; 663 | sourceTree = ""; 664 | }; 665 | /* End PBXVariantGroup section */ 666 | 667 | /* Begin XCBuildConfiguration section */ 668 | 0C506E642097014500E67227 /* Debug */ = { 669 | isa = XCBuildConfiguration; 670 | buildSettings = { 671 | ALWAYS_SEARCH_USER_PATHS = NO; 672 | CLANG_ANALYZER_NONNULL = YES; 673 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 674 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 675 | CLANG_CXX_LIBRARY = "libc++"; 676 | CLANG_ENABLE_MODULES = YES; 677 | CLANG_ENABLE_OBJC_ARC = YES; 678 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 679 | CLANG_WARN_BOOL_CONVERSION = YES; 680 | CLANG_WARN_COMMA = YES; 681 | CLANG_WARN_CONSTANT_CONVERSION = YES; 682 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 683 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 684 | CLANG_WARN_EMPTY_BODY = YES; 685 | CLANG_WARN_ENUM_CONVERSION = YES; 686 | CLANG_WARN_INFINITE_RECURSION = YES; 687 | CLANG_WARN_INT_CONVERSION = YES; 688 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 689 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 690 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 691 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 692 | CLANG_WARN_STRICT_PROTOTYPES = YES; 693 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 694 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 695 | CLANG_WARN_UNREACHABLE_CODE = YES; 696 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 697 | CODE_SIGN_IDENTITY = "iPhone Developer"; 698 | COPY_PHASE_STRIP = NO; 699 | DEBUG_INFORMATION_FORMAT = dwarf; 700 | ENABLE_STRICT_OBJC_MSGSEND = YES; 701 | ENABLE_TESTABILITY = YES; 702 | GCC_C_LANGUAGE_STANDARD = gnu11; 703 | GCC_DYNAMIC_NO_PIC = NO; 704 | GCC_NO_COMMON_BLOCKS = YES; 705 | GCC_OPTIMIZATION_LEVEL = 0; 706 | GCC_PREPROCESSOR_DEFINITIONS = ( 707 | "DEBUG=1", 708 | "$(inherited)", 709 | ); 710 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 711 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 712 | GCC_WARN_UNDECLARED_SELECTOR = YES; 713 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 714 | GCC_WARN_UNUSED_FUNCTION = YES; 715 | GCC_WARN_UNUSED_VARIABLE = YES; 716 | IPHONEOS_DEPLOYMENT_TARGET = 11.2; 717 | MTL_ENABLE_DEBUG_INFO = YES; 718 | ONLY_ACTIVE_ARCH = YES; 719 | SDKROOT = iphoneos; 720 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 721 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 722 | }; 723 | name = Debug; 724 | }; 725 | 0C506E652097014500E67227 /* Release */ = { 726 | isa = XCBuildConfiguration; 727 | buildSettings = { 728 | ALWAYS_SEARCH_USER_PATHS = NO; 729 | CLANG_ANALYZER_NONNULL = YES; 730 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 731 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 732 | CLANG_CXX_LIBRARY = "libc++"; 733 | CLANG_ENABLE_MODULES = YES; 734 | CLANG_ENABLE_OBJC_ARC = YES; 735 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 736 | CLANG_WARN_BOOL_CONVERSION = YES; 737 | CLANG_WARN_COMMA = YES; 738 | CLANG_WARN_CONSTANT_CONVERSION = YES; 739 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 740 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 741 | CLANG_WARN_EMPTY_BODY = YES; 742 | CLANG_WARN_ENUM_CONVERSION = YES; 743 | CLANG_WARN_INFINITE_RECURSION = YES; 744 | CLANG_WARN_INT_CONVERSION = YES; 745 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 746 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 747 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 748 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 749 | CLANG_WARN_STRICT_PROTOTYPES = YES; 750 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 751 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 752 | CLANG_WARN_UNREACHABLE_CODE = YES; 753 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 754 | CODE_SIGN_IDENTITY = "iPhone Developer"; 755 | COPY_PHASE_STRIP = NO; 756 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 757 | ENABLE_NS_ASSERTIONS = NO; 758 | ENABLE_STRICT_OBJC_MSGSEND = YES; 759 | GCC_C_LANGUAGE_STANDARD = gnu11; 760 | GCC_NO_COMMON_BLOCKS = YES; 761 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 762 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 763 | GCC_WARN_UNDECLARED_SELECTOR = YES; 764 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 765 | GCC_WARN_UNUSED_FUNCTION = YES; 766 | GCC_WARN_UNUSED_VARIABLE = YES; 767 | IPHONEOS_DEPLOYMENT_TARGET = 11.2; 768 | MTL_ENABLE_DEBUG_INFO = NO; 769 | SDKROOT = iphoneos; 770 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 771 | VALIDATE_PRODUCT = YES; 772 | }; 773 | name = Release; 774 | }; 775 | 0C506E672097014500E67227 /* Debug */ = { 776 | isa = XCBuildConfiguration; 777 | baseConfigurationReference = B391B5DD98361F650B96D676 /* Pods-CoordinatorExample.debug.xcconfig */; 778 | buildSettings = { 779 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 780 | CODE_SIGN_STYLE = Automatic; 781 | DEVELOPMENT_TEAM = SNLH793PXA; 782 | INFOPLIST_FILE = "$(SRCROOT)/CoordinatorExample/Supporting Files/Info.plist"; 783 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 784 | PRODUCT_BUNDLE_IDENTIFIER = com.rychkovdev.CoordEx; 785 | PRODUCT_NAME = "$(TARGET_NAME)"; 786 | SWIFT_VERSION = 4.0; 787 | TARGETED_DEVICE_FAMILY = 1; 788 | }; 789 | name = Debug; 790 | }; 791 | 0C506E682097014500E67227 /* Release */ = { 792 | isa = XCBuildConfiguration; 793 | baseConfigurationReference = A4322504D4287A54A061F9DB /* Pods-CoordinatorExample.release.xcconfig */; 794 | buildSettings = { 795 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 796 | CODE_SIGN_STYLE = Automatic; 797 | DEVELOPMENT_TEAM = SNLH793PXA; 798 | INFOPLIST_FILE = "$(SRCROOT)/CoordinatorExample/Supporting Files/Info.plist"; 799 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 800 | PRODUCT_BUNDLE_IDENTIFIER = com.rychkovdev.CoordEx; 801 | PRODUCT_NAME = "$(TARGET_NAME)"; 802 | SWIFT_VERSION = 4.0; 803 | TARGETED_DEVICE_FAMILY = 1; 804 | }; 805 | name = Release; 806 | }; 807 | /* End XCBuildConfiguration section */ 808 | 809 | /* Begin XCConfigurationList section */ 810 | 0C506E4F2097014500E67227 /* Build configuration list for PBXProject "CoordinatorExample" */ = { 811 | isa = XCConfigurationList; 812 | buildConfigurations = ( 813 | 0C506E642097014500E67227 /* Debug */, 814 | 0C506E652097014500E67227 /* Release */, 815 | ); 816 | defaultConfigurationIsVisible = 0; 817 | defaultConfigurationName = Release; 818 | }; 819 | 0C506E662097014500E67227 /* Build configuration list for PBXNativeTarget "CoordinatorExample" */ = { 820 | isa = XCConfigurationList; 821 | buildConfigurations = ( 822 | 0C506E672097014500E67227 /* Debug */, 823 | 0C506E682097014500E67227 /* Release */, 824 | ); 825 | defaultConfigurationIsVisible = 0; 826 | defaultConfigurationName = Release; 827 | }; 828 | /* End XCConfigurationList section */ 829 | }; 830 | rootObject = 0C506E4C2097014500E67227 /* Project object */; 831 | } 832 | --------------------------------------------------------------------------------