├── 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 |
--------------------------------------------------------------------------------