├── str.jpg
├── ApplicationCoordinator
├── Application
│ ├── ApplicationCoordinator-Bridging-Header.h
│ ├── AppDelegate.swift
│ └── ApplicationCoordinator.swift
├── Flows
│ ├── Items flow
│ │ ├── ItemDetailView.swift
│ │ ├── ItemsListView.swift
│ │ ├── ItemDetailController.swift
│ │ ├── ItemCoordinator.swift
│ │ └── ItemsListController.swift
│ ├── Settings flow
│ │ ├── SettingsView.swift
│ │ ├── SettingsCoordinator.swift
│ │ └── SettingsController.swift
│ ├── OnboardingFlow
│ │ ├── Controllers
│ │ │ ├── OnboardingView.swift
│ │ │ ├── OnboardingController.swift
│ │ │ ├── StepController.swift
│ │ │ └── PageViewController.swift
│ │ ├── OnboardingCoordinatorOutput.swift
│ │ └── OnboardingCoordinator.swift
│ ├── Login flow
│ │ ├── AuthCoordinatorOutput.swift
│ │ ├── LoginView.swift
│ │ ├── Controllers
│ │ │ ├── TermsView.swift
│ │ │ └── TermsController.swift
│ │ ├── SignUpView.swift
│ │ ├── LoginController.swift
│ │ ├── SignUpController.swift
│ │ └── AuthCoordinator.swift
│ ├── Create flow
│ │ ├── ItemCreateCoordinatorOutput.swift
│ │ ├── ItemCreateView.swift
│ │ ├── ItemCreateController.swift
│ │ └── ItemCreateCoordinator.swift
│ └── MainTabbarFlow
│ │ ├── TabbarView.swift
│ │ ├── TabbarController.swift
│ │ └── TabbarCoordinator.swift
├── Protocols
│ ├── BaseView.swift
│ ├── Coordinator.swift
│ ├── Presentable.swift
│ └── DeepLinkOption.swift
├── Model
│ ├── Setting.swift
│ └── ItemList.swift
├── Factories
│ ├── SettingsModuleFactory.swift
│ ├── ItemCreateModuleFactory.swift
│ ├── OnboardingModuleFactory.swift
│ ├── ItemModuleFactory.swift
│ ├── AuthModuleFactory.swift
│ ├── CoordinatorFactory.swift
│ ├── ModuleFactoryImp.swift
│ └── CoordinatorFactoryImp.swift
├── Images.xcassets
│ ├── Brand Assets.launchimage
│ │ └── Contents.json
│ └── AppIcon.appiconset
│ │ └── Contents.json
├── Extensions
│ ├── NSObjectExtensions.swift
│ ├── UIViewExtensions.swift
│ └── UIViewControllerExtension.swift
├── Storyboards
│ ├── StoryboardsEnum.swift
│ ├── Create.storyboard
│ ├── Settings.storyboard
│ ├── Onboarding.storyboard
│ ├── Items.storyboard
│ └── Auth.storyboard
├── Presenters
│ ├── Router.swift
│ └── RouterImp.swift
├── Parents
│ └── BaseCoordinator.swift
├── Resources
│ └── Info.plist
└── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
├── ApplicationCoordinator.xcodeproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
└── project.pbxproj
├── ApplicationCoordinatorTests
├── Info.plist
├── SettingsFlow
│ └── SettingsCoordinatorTest.swift
├── Parent
│ └── BaseCoordinatorTest.swift
├── ItemsFlow
│ └── ItemCoordinatorTest.swift
├── Router
│ ├── RouterTest.swift
│ └── RouterMock.swift
└── LoginFlow
│ └── AuthCoordinatorTest.swift
├── LICENSE
├── .gitignore
└── README.md
/str.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndreyPanov/ApplicationCoordinator/HEAD/str.jpg
--------------------------------------------------------------------------------
/ApplicationCoordinator/Application/ApplicationCoordinator-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | @import UIKit;
2 |
--------------------------------------------------------------------------------
/ApplicationCoordinator/Flows/Items flow/ItemDetailView.swift:
--------------------------------------------------------------------------------
1 | protocol ItemDetailView: BaseView { }
2 |
--------------------------------------------------------------------------------
/ApplicationCoordinator/Flows/Settings flow/SettingsView.swift:
--------------------------------------------------------------------------------
1 | protocol SettingsView: BaseView { }
2 |
3 |
--------------------------------------------------------------------------------
/ApplicationCoordinator/Protocols/BaseView.swift:
--------------------------------------------------------------------------------
1 | protocol BaseView: NSObjectProtocol, Presentable { }
2 |
--------------------------------------------------------------------------------
/ApplicationCoordinator/Model/Setting.swift:
--------------------------------------------------------------------------------
1 | struct Setting {
2 |
3 | let title: String
4 | let subtitle: String
5 | }
6 |
--------------------------------------------------------------------------------
/ApplicationCoordinator/Model/ItemList.swift:
--------------------------------------------------------------------------------
1 | struct ItemList {
2 |
3 | let title: String
4 | let subtitle: String
5 | }
6 |
--------------------------------------------------------------------------------
/ApplicationCoordinator/Factories/SettingsModuleFactory.swift:
--------------------------------------------------------------------------------
1 | protocol SettingsModuleFactory {
2 | func makeSettingsOutput() -> SettingsView
3 | }
4 |
--------------------------------------------------------------------------------
/ApplicationCoordinator/Factories/ItemCreateModuleFactory.swift:
--------------------------------------------------------------------------------
1 | protocol ItemCreateModuleFactory {
2 | func makeItemAddOutput() -> ItemCreateView
3 | }
4 |
--------------------------------------------------------------------------------
/ApplicationCoordinator/Factories/OnboardingModuleFactory.swift:
--------------------------------------------------------------------------------
1 | protocol OnboardingModuleFactory {
2 | func makeOnboardingModule() -> OnboardingView
3 | }
4 |
--------------------------------------------------------------------------------
/ApplicationCoordinator/Protocols/Coordinator.swift:
--------------------------------------------------------------------------------
1 | protocol Coordinator: AnyObject {
2 | func start()
3 | func start(with option: DeepLinkOption?)
4 | }
5 |
--------------------------------------------------------------------------------
/ApplicationCoordinator/Flows/OnboardingFlow/Controllers/OnboardingView.swift:
--------------------------------------------------------------------------------
1 | protocol OnboardingView: BaseView {
2 | var onFinish: (() -> Void)? { get set }
3 | }
4 |
--------------------------------------------------------------------------------
/ApplicationCoordinator/Flows/Login flow/AuthCoordinatorOutput.swift:
--------------------------------------------------------------------------------
1 | protocol AuthCoordinatorOutput: AnyObject {
2 | var finishFlow: (() -> Void)? { get set }
3 | }
4 |
--------------------------------------------------------------------------------
/ApplicationCoordinator/Flows/Create flow/ItemCreateCoordinatorOutput.swift:
--------------------------------------------------------------------------------
1 | protocol ItemCreateCoordinatorOutput: AnyObject {
2 | var finishFlow: ((ItemList?)->())? { get set }
3 | }
4 |
--------------------------------------------------------------------------------
/ApplicationCoordinator/Flows/OnboardingFlow/OnboardingCoordinatorOutput.swift:
--------------------------------------------------------------------------------
1 | protocol OnboardingCoordinatorOutput: AnyObject {
2 | var finishFlow: (() -> Void)? { get set }
3 | }
4 |
--------------------------------------------------------------------------------
/ApplicationCoordinator/Images.xcassets/Brand Assets.launchimage/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 |
4 | ],
5 | "info" : {
6 | "version" : 1,
7 | "author" : "xcode"
8 | }
9 | }
--------------------------------------------------------------------------------
/ApplicationCoordinator/Flows/Login flow/LoginView.swift:
--------------------------------------------------------------------------------
1 | protocol LoginView: BaseView {
2 | var onCompleteAuth: (() -> Void)? { get set }
3 | var onSignUpButtonTap: (() -> Void)? { get set }
4 | }
5 |
--------------------------------------------------------------------------------
/ApplicationCoordinator/Factories/ItemModuleFactory.swift:
--------------------------------------------------------------------------------
1 | protocol ItemModuleFactory {
2 | func makeItemsOutput() -> ItemsListView
3 | func makeItemDetailOutput(item: ItemList) -> ItemDetailView
4 | }
5 |
--------------------------------------------------------------------------------
/ApplicationCoordinator/Flows/Items flow/ItemsListView.swift:
--------------------------------------------------------------------------------
1 | protocol ItemsListView: BaseView {
2 | var onItemSelect: ((ItemList) -> ())? { get set }
3 | var onCreateItem: (() -> Void)? { get set }
4 | }
5 |
--------------------------------------------------------------------------------
/ApplicationCoordinator/Flows/Login flow/Controllers/TermsView.swift:
--------------------------------------------------------------------------------
1 | protocol TermsView: BaseView {
2 |
3 | var confirmed: Bool { get set }
4 | var onConfirmChanged: ((Bool) -> ())? { get set }
5 | }
6 |
--------------------------------------------------------------------------------
/ApplicationCoordinator/Extensions/NSObjectExtensions.swift:
--------------------------------------------------------------------------------
1 | extension NSObject {
2 |
3 | class var nameOfClass: String {
4 | return NSStringFromClass(self).components(separatedBy: ".").last!
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/ApplicationCoordinator/Factories/AuthModuleFactory.swift:
--------------------------------------------------------------------------------
1 | protocol AuthModuleFactory {
2 | func makeLoginOutput() -> LoginView
3 | func makeSignUpHandler() -> SignUpView
4 | func makeTermsOutput() -> TermsView
5 | }
6 |
--------------------------------------------------------------------------------
/ApplicationCoordinator/Flows/Create flow/ItemCreateView.swift:
--------------------------------------------------------------------------------
1 | protocol ItemCreateView: BaseView {
2 | var onHideButtonTap: (() -> Void)? { get set }
3 | var onCompleteCreateItem: ((ItemList) -> ())? { get set }
4 | }
5 |
--------------------------------------------------------------------------------
/ApplicationCoordinator/Storyboards/StoryboardsEnum.swift:
--------------------------------------------------------------------------------
1 | enum Storyboards: String {
2 | case main = "Main"
3 | case items = "Items"
4 | case auth = "Auth"
5 | case onboarding = "Onboarding"
6 | case create = "Create"
7 | case settings = "Settings"
8 | }
9 |
--------------------------------------------------------------------------------
/ApplicationCoordinator/Protocols/Presentable.swift:
--------------------------------------------------------------------------------
1 | protocol Presentable {
2 | func toPresent() -> UIViewController?
3 | }
4 |
5 | extension UIViewController: Presentable {
6 |
7 | func toPresent() -> UIViewController? {
8 | return self
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/ApplicationCoordinator/Flows/Login flow/SignUpView.swift:
--------------------------------------------------------------------------------
1 | protocol SignUpView: BaseView {
2 |
3 | var confirmed: Bool { get set }
4 | var onSignUpComplete: (() -> Void)? { get set }
5 | var onTermsButtonTap: (() -> Void)? { get set }
6 |
7 | func conformTermsAgreement(_ agree: Bool)
8 | }
9 |
--------------------------------------------------------------------------------
/ApplicationCoordinator.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ApplicationCoordinator/Flows/MainTabbarFlow/TabbarView.swift:
--------------------------------------------------------------------------------
1 | protocol TabbarView: AnyObject {
2 | var onItemFlowSelect: ((UINavigationController) -> ())? { get set }
3 | var onSettingsFlowSelect: ((UINavigationController) -> ())? { get set }
4 | var onViewDidLoad: ((UINavigationController) -> ())? { get set }
5 | }
6 |
--------------------------------------------------------------------------------
/ApplicationCoordinator/Flows/Items flow/ItemDetailController.swift:
--------------------------------------------------------------------------------
1 | final class ItemDetailController: UIViewController, ItemDetailView {
2 |
3 | //controller handler
4 | var itemList: ItemList?
5 |
6 | override func viewDidLoad() {
7 | super.viewDidLoad()
8 |
9 | title = itemList?.title ?? "Detail"
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/ApplicationCoordinator.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ApplicationCoordinator/Flows/OnboardingFlow/Controllers/OnboardingController.swift:
--------------------------------------------------------------------------------
1 | class OnboardingController: UIViewController, OnboardingView {
2 |
3 | var onFinish: (() -> Void)?
4 |
5 | override func viewDidLoad() {
6 | super.viewDidLoad()
7 | }
8 |
9 | @IBAction func finishTapped(_ sender: Any) {
10 | onFinish?()
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/ApplicationCoordinator/Flows/Login flow/Controllers/TermsController.swift:
--------------------------------------------------------------------------------
1 | class TermsController: UIViewController, TermsView {
2 |
3 | @IBOutlet weak var termsSwitch: UISwitch! {
4 | didSet { termsSwitch.isOn = confirmed }
5 | }
6 | var confirmed = false
7 | var onConfirmChanged: ((Bool) -> ())?
8 |
9 | @IBAction func termsSwitchValueChanged(_ sender: UISwitch) {
10 | onConfirmChanged?(sender.isOn)
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/ApplicationCoordinator/Flows/OnboardingFlow/Controllers/StepController.swift:
--------------------------------------------------------------------------------
1 | class StepController: UIViewController {
2 |
3 | @IBOutlet weak var titleLabel: UILabel!
4 | var titleText: String? {
5 | get { return titleLabel.text }
6 | set { titleLabel.text = newValue }
7 | }
8 |
9 | override func viewDidLoad() {
10 | super.viewDidLoad()
11 | }
12 |
13 | @IBAction func skipTapped(_ sender: Any) {
14 | }
15 |
16 | @IBAction func nextTapped(_ sender: Any) {
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/ApplicationCoordinator/Extensions/UIViewExtensions.swift:
--------------------------------------------------------------------------------
1 | extension UIView {
2 |
3 | private class func viewInNibNamed(_ nibNamed: String) -> T {
4 | return Bundle.main.loadNibNamed(nibNamed, owner: nil, options: nil)!.first as! T
5 | }
6 |
7 | class func nib() -> Self {
8 | return viewInNibNamed(nameOfClass)
9 | }
10 |
11 | class func nib(_ frame: CGRect) -> Self {
12 | let view = nib()
13 | view.frame = frame
14 | view.layoutIfNeeded()
15 | return view
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/ApplicationCoordinator/Flows/Login flow/LoginController.swift:
--------------------------------------------------------------------------------
1 | final class LoginController: UIViewController, LoginView {
2 |
3 | //controller handler
4 | var onCompleteAuth: (() -> Void)?
5 | var onSignUpButtonTap: (() -> Void)?
6 |
7 | @IBAction func loginButtonClicked(_ sender: AnyObject) { onCompleteAuth?() }
8 | @IBAction func signUpClicked(_ sender: AnyObject) { onSignUpButtonTap?() }
9 |
10 | override func viewDidLoad() {
11 | super.viewDidLoad()
12 |
13 | title = "Login"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/ApplicationCoordinator/Flows/Settings flow/SettingsCoordinator.swift:
--------------------------------------------------------------------------------
1 | final class SettingsCoordinator: BaseCoordinator {
2 |
3 | private let factory: SettingsModuleFactory
4 | private let router: Router
5 |
6 | init(router: Router, factory: SettingsModuleFactory) {
7 | self.factory = factory
8 | self.router = router
9 | }
10 |
11 | override func start() {
12 | showSettings()
13 | }
14 |
15 | //MARK: - Run current flow's controllers
16 |
17 | private func showSettings() {
18 | let settingsFlowOutput = factory.makeSettingsOutput()
19 | router.setRootModule(settingsFlowOutput)
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/ApplicationCoordinator/Flows/OnboardingFlow/OnboardingCoordinator.swift:
--------------------------------------------------------------------------------
1 | class OnboardingCoordinator: BaseCoordinator, OnboardingCoordinatorOutput {
2 |
3 | var finishFlow: (() -> Void)?
4 |
5 | private let factory: OnboardingModuleFactory
6 | private let router: Router
7 |
8 | init(with factory: OnboardingModuleFactory, router: Router) {
9 | self.factory = factory
10 | self.router = router
11 | }
12 |
13 | override func start() {
14 | showOnboarding()
15 | }
16 |
17 | func showOnboarding() {
18 | let onboardingModule = factory.makeOnboardingModule()
19 | onboardingModule.onFinish = { [weak self] in
20 | self?.finishFlow?()
21 | }
22 | router.setRootModule(onboardingModule.toPresent())
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/ApplicationCoordinator/Flows/Create flow/ItemCreateController.swift:
--------------------------------------------------------------------------------
1 | final class ItemCreateController: UIViewController, ItemCreateView {
2 |
3 | //controller handler
4 | var onHideButtonTap: (() -> Void)?
5 | var onCompleteCreateItem: ((ItemList) -> ())?
6 |
7 | override func viewDidLoad() {
8 | super.viewDidLoad()
9 |
10 | title = "Create"
11 | navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Hide", style: .plain, target: self, action: #selector(ItemCreateController.hideButtonClicked(_:)))
12 | }
13 |
14 | @IBAction func hideButtonClicked(_ sender: UIBarButtonItem) {
15 | onHideButtonTap?()
16 | }
17 |
18 | @IBAction func createButtonClicked(_ sender: UIBarButtonItem) {
19 | onCompleteCreateItem?(ItemList(title: "", subtitle: ""))
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/ApplicationCoordinatorTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/ApplicationCoordinator/Presenters/Router.swift:
--------------------------------------------------------------------------------
1 | protocol Router: 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?, hideBottomBar: Bool)
8 | func push(_ module: Presentable?, animated: Bool)
9 | func push(_ module: Presentable?, animated: Bool, completion: (() -> Void)?)
10 | func push(_ module: Presentable?, animated: Bool, hideBottomBar: Bool, completion: (() -> Void)?)
11 |
12 | func popModule()
13 | func popModule(animated: Bool)
14 |
15 | func dismissModule()
16 | func dismissModule(animated: Bool, completion: (() -> Void)?)
17 |
18 | func setRootModule(_ module: Presentable?)
19 | func setRootModule(_ module: Presentable?, hideBar: Bool)
20 |
21 | func popToRootModule(animated: Bool)
22 | }
23 |
--------------------------------------------------------------------------------
/ApplicationCoordinator/Extensions/UIViewControllerExtension.swift:
--------------------------------------------------------------------------------
1 | extension UIViewController {
2 |
3 | private class func instantiateControllerInStoryboard(_ storyboard: UIStoryboard, identifier: String) -> T {
4 | return storyboard.instantiateViewController(withIdentifier: identifier) as! T
5 | }
6 |
7 | class func controllerInStoryboard(_ storyboard: UIStoryboard, identifier: String) -> Self {
8 | return instantiateControllerInStoryboard(storyboard, identifier: identifier)
9 | }
10 |
11 | class func controllerInStoryboard(_ storyboard: UIStoryboard) -> Self {
12 | return controllerInStoryboard(storyboard, identifier: nameOfClass)
13 | }
14 |
15 | class func controllerFromStoryboard(_ storyboard: Storyboards) -> Self {
16 | return controllerInStoryboard(UIStoryboard(name: storyboard.rawValue, bundle: nil), identifier: nameOfClass)
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/ApplicationCoordinator/Flows/Create flow/ItemCreateCoordinator.swift:
--------------------------------------------------------------------------------
1 | final class ItemCreateCoordinator: BaseCoordinator, ItemCreateCoordinatorOutput {
2 |
3 | var finishFlow: ((ItemList?)->())?
4 |
5 | private let factory: ItemCreateModuleFactory
6 | private let router: Router
7 |
8 | init(router: Router, factory: ItemCreateModuleFactory) {
9 | self.factory = factory
10 | self.router = router
11 | }
12 |
13 | override func start() {
14 | showCreate()
15 | }
16 |
17 | //MARK: - Run current flow's controllers
18 |
19 | private func showCreate() {
20 | let createItemOutput = factory.makeItemAddOutput()
21 | createItemOutput.onCompleteCreateItem = { [weak self] item in
22 | self?.finishFlow?(item)
23 | }
24 | createItemOutput.onHideButtonTap = { [weak self] in
25 | self?.finishFlow?(nil)
26 | }
27 | router.setRootModule(createItemOutput)
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/ApplicationCoordinator/Factories/CoordinatorFactory.swift:
--------------------------------------------------------------------------------
1 | protocol CoordinatorFactory {
2 |
3 | func makeTabbarCoordinator() -> (configurator: Coordinator, toPresent: Presentable?)
4 | func makeAuthCoordinatorBox(router: Router) -> Coordinator & AuthCoordinatorOutput
5 |
6 | func makeOnboardingCoordinator(router: Router) -> Coordinator & OnboardingCoordinatorOutput
7 |
8 | func makeItemCoordinator(navController: UINavigationController?) -> Coordinator
9 | func makeItemCoordinator() -> Coordinator
10 |
11 | func makeSettingsCoordinator() -> Coordinator
12 | func makeSettingsCoordinator(navController: UINavigationController?) -> Coordinator
13 |
14 | func makeItemCreationCoordinatorBox() ->
15 | (configurator: Coordinator & ItemCreateCoordinatorOutput,
16 | toPresent: Presentable?)
17 |
18 | func makeItemCreationCoordinatorBox(navController: UINavigationController?) ->
19 | (configurator: Coordinator & ItemCreateCoordinatorOutput,
20 | toPresent: Presentable?)
21 | }
22 |
--------------------------------------------------------------------------------
/ApplicationCoordinator/Flows/MainTabbarFlow/TabbarController.swift:
--------------------------------------------------------------------------------
1 | final class TabbarController: UITabBarController, UITabBarControllerDelegate, TabbarView {
2 |
3 | var onItemFlowSelect: ((UINavigationController) -> ())?
4 | var onSettingsFlowSelect: ((UINavigationController) -> ())?
5 | var onViewDidLoad: ((UINavigationController) -> ())?
6 |
7 | override func viewDidLoad() {
8 | super.viewDidLoad()
9 |
10 | delegate = self
11 | if let controller = customizableViewControllers?.first as? UINavigationController {
12 | onViewDidLoad?(controller)
13 | }
14 | }
15 |
16 | func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
17 | guard let controller = viewControllers?[selectedIndex] as? UINavigationController else { return }
18 |
19 | if selectedIndex == 0 {
20 | onItemFlowSelect?(controller)
21 | }
22 | else if selectedIndex == 1 {
23 | onSettingsFlowSelect?(controller)
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/ApplicationCoordinator/Images.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "29x29",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "29x29",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "40x40",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "40x40",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "size" : "60x60",
36 | "scale" : "2x"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "size" : "60x60",
41 | "scale" : "3x"
42 | }
43 | ],
44 | "info" : {
45 | "version" : 1,
46 | "author" : "xcode"
47 | }
48 | }
--------------------------------------------------------------------------------
/ApplicationCoordinator/Flows/Login flow/SignUpController.swift:
--------------------------------------------------------------------------------
1 | final class SignUpController: UIViewController, SignUpView {
2 |
3 | //controller handler
4 | var onSignUpComplete: (() -> Void)?
5 | var onTermsButtonTap: (() -> Void)?
6 |
7 | @IBOutlet weak var termsLabel: UILabel!
8 | @IBOutlet weak var signUpButton: UIButton!
9 |
10 | var confirmed = false {
11 | didSet {
12 | termsLabel.isHidden = !confirmed
13 | signUpButton.isEnabled = confirmed
14 | }
15 | }
16 |
17 | override func viewDidLoad() {
18 | super.viewDidLoad()
19 |
20 | title = "SignUp"
21 | termsLabel.isHidden = true
22 | signUpButton.isEnabled = false
23 | }
24 |
25 | @IBAction func signUpClicked(_ sender: AnyObject) {
26 | if confirmed {
27 | onSignUpComplete?()
28 | }
29 | }
30 |
31 | @IBAction func termsButtonClicked(_ sender: AnyObject) {
32 | onTermsButtonTap?()
33 | }
34 |
35 | func conformTermsAgreement(_ agree: Bool) {
36 | confirmed = agree
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/ApplicationCoordinatorTests/SettingsFlow/SettingsCoordinatorTest.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SettingsCoordinatorTest.swift
3 | // ApplicationCoordinator
4 | //
5 | // Created by Andrey on 04.09.16.
6 | // Copyright © 2016 Andrey Panov. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import ApplicationCoordinator
11 |
12 | class SettingsCoordinatorTest: XCTestCase {
13 |
14 | private var coordinator: Coordinator!
15 | private var router: RouterMock!
16 |
17 | override func setUp() {
18 | super.setUp()
19 |
20 | router = RouterMockImp()
21 | coordinator = SettingsCoordinator(router: router, factory: ModuleFactoryImp())
22 | }
23 |
24 | override func tearDown() {
25 | coordinator = nil
26 | router = nil
27 |
28 | super.tearDown()
29 | }
30 |
31 | func testStart() {
32 | coordinator.start()
33 | // after start() call coordinator must push SettingsController
34 | XCTAssertTrue(router.navigationStack.first is SettingsController)
35 | XCTAssertTrue(router.navigationStack.count == 1)
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Andrey Panov
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 |
--------------------------------------------------------------------------------
/ApplicationCoordinator/Parents/BaseCoordinator.swift:
--------------------------------------------------------------------------------
1 | class BaseCoordinator: Coordinator {
2 |
3 | var childCoordinators: [Coordinator] = []
4 |
5 | func start() {
6 | start(with: nil)
7 | }
8 |
9 | func start(with option: DeepLinkOption?) { }
10 |
11 | // add only unique object
12 | func addDependency(_ coordinator: Coordinator) {
13 | guard !childCoordinators.contains(where: { $0 === coordinator }) else { return }
14 | childCoordinators.append(coordinator)
15 | }
16 |
17 | func removeDependency(_ coordinator: Coordinator?) {
18 | guard
19 | childCoordinators.isEmpty == false,
20 | let coordinator = coordinator
21 | else { return }
22 |
23 | // Clear child-coordinators recursively
24 | if let coordinator = coordinator as? BaseCoordinator, !coordinator.childCoordinators.isEmpty {
25 | coordinator.childCoordinators
26 | .filter({ $0 !== coordinator })
27 | .forEach({ coordinator.removeDependency($0) })
28 | }
29 | for (index, element) in childCoordinators.enumerated() where element === coordinator {
30 | childCoordinators.remove(at: index)
31 | break
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/ApplicationCoordinator/Flows/OnboardingFlow/Controllers/PageViewController.swift:
--------------------------------------------------------------------------------
1 | class PageController: UIViewController {
2 |
3 | var pageViewController: UIPageViewController!
4 |
5 | override func viewDidLoad() {
6 | super.viewDidLoad()
7 |
8 | pageViewController = UIPageViewController(
9 | transitionStyle: .scroll,
10 | navigationOrientation: .horizontal,
11 | options: nil
12 | )
13 | pageViewController.delegate = self
14 | addChild(pageViewController)
15 | view.addSubview(pageViewController.view)
16 | }
17 | }
18 |
19 | extension PageController: UIPageViewControllerDelegate, UIPageViewControllerDataSource {
20 |
21 | func pageViewController(_ pageViewController: UIPageViewController,
22 | viewControllerBefore viewController: UIViewController) -> UIViewController? {
23 | return UIViewController()
24 | }
25 |
26 | func pageViewController(_ pageViewController: UIPageViewController,
27 | viewControllerAfter viewController: UIViewController) -> UIViewController? {
28 | return UIViewController()
29 | }
30 |
31 | func presentationCount(for pageViewController: UIPageViewController) -> Int {
32 | return 3
33 | }
34 |
35 | func presentationIndex(for pageViewController: UIPageViewController) -> Int {
36 | return 2
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/ApplicationCoordinator/Factories/ModuleFactoryImp.swift:
--------------------------------------------------------------------------------
1 | final class ModuleFactoryImp:
2 | AuthModuleFactory,
3 | OnboardingModuleFactory,
4 | ItemModuleFactory,
5 | ItemCreateModuleFactory,
6 | SettingsModuleFactory {
7 |
8 | func makeLoginOutput() -> LoginView {
9 | return LoginController.controllerFromStoryboard(.auth)
10 | }
11 |
12 | func makeSignUpHandler() -> SignUpView {
13 | return SignUpController.controllerFromStoryboard(.auth)
14 | }
15 |
16 | func makeOnboardingModule() -> OnboardingView {
17 | return OnboardingController.controllerFromStoryboard(.onboarding)
18 | }
19 |
20 | func makeTermsOutput() -> TermsView {
21 | return TermsController.controllerFromStoryboard(.auth)
22 | }
23 |
24 | func makeItemsOutput() -> ItemsListView {
25 | return ItemsListController.controllerFromStoryboard(.items)
26 | }
27 |
28 | func makeItemDetailOutput(item: ItemList) -> ItemDetailView {
29 |
30 | let controller = ItemDetailController.controllerFromStoryboard(.items)
31 | controller.itemList = item
32 | return controller
33 | }
34 |
35 | func makeItemAddOutput() -> ItemCreateView {
36 | return ItemCreateController.controllerFromStoryboard(.create)
37 | }
38 |
39 | func makeSettingsOutput() -> SettingsView {
40 | return SettingsController.controllerFromStoryboard(.settings)
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/ApplicationCoordinator/Flows/MainTabbarFlow/TabbarCoordinator.swift:
--------------------------------------------------------------------------------
1 | class TabbarCoordinator: BaseCoordinator {
2 |
3 | private let tabbarView: TabbarView
4 | private let coordinatorFactory: CoordinatorFactory
5 |
6 | init(tabbarView: TabbarView, coordinatorFactory: CoordinatorFactory) {
7 | self.tabbarView = tabbarView
8 | self.coordinatorFactory = coordinatorFactory
9 | }
10 |
11 | override func start() {
12 | tabbarView.onViewDidLoad = runItemFlow()
13 | tabbarView.onItemFlowSelect = runItemFlow()
14 | tabbarView.onSettingsFlowSelect = runSettingsFlow()
15 | }
16 |
17 | private func runItemFlow() -> ((UINavigationController) -> ()) {
18 | return { [unowned self] navController in
19 | if navController.viewControllers.isEmpty == true {
20 | let itemCoordinator = self.coordinatorFactory.makeItemCoordinator(navController: navController)
21 | self.addDependency(itemCoordinator)
22 | itemCoordinator.start()
23 | }
24 | }
25 | }
26 |
27 | private func runSettingsFlow() -> ((UINavigationController) -> ()) {
28 | return { [unowned self] navController in
29 | if navController.viewControllers.isEmpty == true {
30 | let settingsCoordinator = self.coordinatorFactory.makeSettingsCoordinator(navController: navController)
31 | self.addDependency(settingsCoordinator)
32 | settingsCoordinator.start()
33 | }
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/ApplicationCoordinator/Resources/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIcons
10 |
11 | CFBundleIcons~ipad
12 |
13 | CFBundleIdentifier
14 | $(PRODUCT_BUNDLE_IDENTIFIER)
15 | CFBundleInfoDictionaryVersion
16 | 6.0
17 | CFBundleName
18 | $(PRODUCT_NAME)
19 | CFBundlePackageType
20 | APPL
21 | CFBundleShortVersionString
22 | 1.0
23 | CFBundleSignature
24 | ????
25 | CFBundleVersion
26 | 1
27 | LSRequiresIPhoneOS
28 |
29 | UILaunchStoryboardName
30 | LaunchScreen
31 | UIMainStoryboardFile
32 | Main
33 | UIRequiredDeviceCapabilities
34 |
35 | armv7
36 |
37 | UISupportedInterfaceOrientations
38 |
39 | UIInterfaceOrientationPortrait
40 | UIInterfaceOrientationLandscapeLeft
41 | UIInterfaceOrientationLandscapeRight
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/ApplicationCoordinator/Flows/Settings flow/SettingsController.swift:
--------------------------------------------------------------------------------
1 | final class SettingsController: UIViewController, SettingsView {
2 |
3 | //controller handler
4 |
5 | @IBOutlet weak var tableView: UITableView!
6 | //mock datasource
7 | var settings: [[Setting]] = [[]]
8 |
9 | override func viewDidLoad() {
10 | super.viewDidLoad()
11 |
12 | title = "Settings"
13 | makeMockData()
14 | }
15 |
16 | func makeMockData() {
17 |
18 | let settings1 = (0...3).map { index in return Setting(title: "Setting № \(index)", subtitle: "Setting descripton") }
19 | let settings2 = (0...5).map { index in return Setting(title: "Setting № \(index)", subtitle: "Setting descripton") }
20 | settings.append(settings1)
21 | settings.append(settings2)
22 | }
23 | }
24 |
25 | extension SettingsController: UITableViewDelegate, UITableViewDataSource {
26 |
27 | func numberOfSections(in tableView: UITableView) -> Int {
28 | return settings.count
29 | }
30 |
31 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
32 | return settings[section].count
33 | }
34 |
35 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
36 | let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
37 |
38 | let setting = settings[(indexPath as NSIndexPath).section][(indexPath as NSIndexPath).row]
39 | cell.textLabel?.text = setting.title
40 | cell.detailTextLabel?.text = setting.subtitle
41 | return cell
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/ApplicationCoordinator/Flows/Login flow/AuthCoordinator.swift:
--------------------------------------------------------------------------------
1 | final class AuthCoordinator: BaseCoordinator, AuthCoordinatorOutput {
2 |
3 | var finishFlow: (() -> Void)?
4 |
5 | private let factory: AuthModuleFactory
6 | private let router: Router
7 | private weak var signUpView: SignUpView?
8 |
9 | init(router: Router, factory: AuthModuleFactory) {
10 | self.factory = factory
11 | self.router = router
12 | }
13 |
14 | override func start() {
15 | showLogin()
16 | }
17 |
18 | //MARK: - Run current flow's controllers
19 |
20 | private func showLogin() {
21 | let loginOutput = factory.makeLoginOutput()
22 | loginOutput.onCompleteAuth = { [weak self] in
23 | self?.finishFlow?()
24 | }
25 | loginOutput.onSignUpButtonTap = { [weak self] in
26 | self?.showSignUp()
27 | }
28 | router.setRootModule(loginOutput)
29 | }
30 |
31 | private func showSignUp() {
32 | signUpView = factory.makeSignUpHandler()
33 | signUpView?.onSignUpComplete = { [weak self] in
34 | self?.finishFlow?()
35 | }
36 | signUpView?.onTermsButtonTap = { [weak self] in
37 | self?.showTerms()
38 | }
39 | router.push(signUpView)
40 | }
41 |
42 | private func showTerms() {
43 | let termsOutput = factory.makeTermsOutput()
44 | termsOutput.confirmed = self.signUpView?.confirmed ?? false
45 |
46 | termsOutput.onConfirmChanged = { [weak self] confirmed in
47 | self?.signUpView?.conformTermsAgreement(confirmed)
48 | }
49 | router.push(termsOutput, animated: true)
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/ApplicationCoordinator/Protocols/DeepLinkOption.swift:
--------------------------------------------------------------------------------
1 | struct DeepLinkURLConstants {
2 | static let Onboarding = "onboarding"
3 | static let Items = "items"
4 | static let Item = "item"
5 | static let Settings = "settings"
6 | static let Login = "login"
7 | static let Terms = "terms"
8 | static let SignUp = "signUp"
9 | }
10 |
11 | enum DeepLinkOption {
12 |
13 | case onboarding
14 | case items
15 | case settings
16 | case login
17 | case terms
18 | case signUp
19 | case item(String?)
20 |
21 | static func build(with userActivity: NSUserActivity) -> DeepLinkOption? {
22 | if userActivity.activityType == NSUserActivityTypeBrowsingWeb,
23 | let url = userActivity.webpageURL,
24 | let _ = URLComponents(url: url, resolvingAgainstBaseURL: true) {
25 | //TODO: extract string and match with DeepLinkURLConstants
26 | }
27 | return nil
28 | }
29 |
30 | static func build(with dict: [String : AnyObject]?) -> DeepLinkOption? {
31 | guard let id = dict?["launch_id"] as? String else { return nil }
32 |
33 | let itemID = dict?["item_id"] as? String
34 |
35 | switch id {
36 | case DeepLinkURLConstants.Onboarding: return .onboarding
37 | case DeepLinkURLConstants.Items: return .items
38 | case DeepLinkURLConstants.Item: return .item(itemID)
39 | case DeepLinkURLConstants.Settings: return .settings
40 | case DeepLinkURLConstants.Login: return .login
41 | case DeepLinkURLConstants.Terms: return .terms
42 | case DeepLinkURLConstants.SignUp: return .signUp
43 | default: return nil
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## macOS
6 | .DS_STORE
7 |
8 | ## Build generated
9 | build/
10 | DerivedData
11 |
12 | ## Various settings
13 | *.pbxuser
14 | !default.pbxuser
15 | *.mode1v3
16 | !default.mode1v3
17 | *.mode2v3
18 | !default.mode2v3
19 | *.perspectivev3
20 | !default.perspectivev3
21 | xcuserdata
22 |
23 | ## Other
24 | *.xccheckout
25 | *.moved-aside
26 | *.xcuserstate
27 | *.xcscmblueprint
28 |
29 | ## Obj-C/Swift specific
30 | *.hmap
31 | *.ipa
32 |
33 | ## Playgrounds
34 | timeline.xctimeline
35 | playground.xcworkspace
36 |
37 | # Swift Package Manager
38 | #
39 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
40 | # Packages/
41 | .build/
42 |
43 | # CocoaPods
44 | #
45 | # We recommend against adding the Pods directory to your .gitignore. However
46 | # you should judge for yourself, the pros and cons are mentioned at:
47 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
48 | #
49 | # Pods/
50 |
51 | # Carthage
52 | #
53 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
54 | # Carthage/Checkouts
55 |
56 | Carthage/Build
57 |
58 | # fastlane
59 | #
60 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
61 | # screenshots whenever they are needed.
62 | # For more information about the recommended setup visit:
63 | # https://github.com/fastlane/fastlane/blob/master/docs/Gitignore.md
64 |
65 | fastlane/report.xml
66 | fastlane/screenshots
67 |
--------------------------------------------------------------------------------
/ApplicationCoordinator/Flows/Items flow/ItemCoordinator.swift:
--------------------------------------------------------------------------------
1 | final class ItemCoordinator: BaseCoordinator {
2 |
3 | private let factory: ItemModuleFactory
4 | private let coordinatorFactory: CoordinatorFactory
5 | private let router: Router
6 |
7 | init(router: Router, factory: ItemModuleFactory, coordinatorFactory: CoordinatorFactory) {
8 | self.router = router
9 | self.factory = factory
10 | self.coordinatorFactory = coordinatorFactory
11 | }
12 |
13 | override func start() {
14 | showItemList()
15 | }
16 |
17 | //MARK: - Run current flow's controllers
18 |
19 | private func showItemList() {
20 |
21 | let itemsOutput = factory.makeItemsOutput()
22 | itemsOutput.onItemSelect = { [weak self] (item) in
23 | self?.showItemDetail(item)
24 | }
25 | itemsOutput.onCreateItem = { [weak self] in
26 | self?.runCreationFlow()
27 | }
28 | router.setRootModule(itemsOutput)
29 | }
30 |
31 | private func showItemDetail(_ item: ItemList) {
32 |
33 | let itemDetailFlowOutput = factory.makeItemDetailOutput(item: item)
34 | router.push(itemDetailFlowOutput, hideBottomBar: true)
35 | }
36 |
37 | //MARK: - Run coordinators (switch to another flow)
38 |
39 | private func runCreationFlow() {
40 |
41 | let (coordinator, module) = coordinatorFactory.makeItemCreationCoordinatorBox()
42 | coordinator.finishFlow = { [weak self, weak coordinator] item in
43 |
44 | self?.router.dismissModule()
45 | self?.removeDependency(coordinator)
46 | if let item = item {
47 | self?.showItemDetail(item)
48 | }
49 | }
50 | addDependency(coordinator)
51 | router.present(module)
52 | coordinator.start()
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/ApplicationCoordinator/Application/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | @UIApplicationMain
2 | class AppDelegate: UIResponder, UIApplicationDelegate {
3 |
4 | var window: UIWindow?
5 | var rootController: UINavigationController {
6 | return self.window!.rootViewController as! UINavigationController
7 | }
8 |
9 | private lazy var applicationCoordinator: Coordinator = self.makeCoordinator()
10 |
11 | func application(_ application: UIApplication,
12 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
13 | let notification = launchOptions?[.remoteNotification] as? [String: AnyObject]
14 | let deepLink = DeepLinkOption.build(with: notification)
15 | applicationCoordinator.start(with: deepLink)
16 | return true
17 | }
18 |
19 | private func makeCoordinator() -> Coordinator {
20 | return ApplicationCoordinator(
21 | router: RouterImp(rootController: self.rootController),
22 | coordinatorFactory: CoordinatorFactoryImp()
23 | )
24 | }
25 |
26 | //MARK: Handle push notifications and deep links
27 | func application(_ application: UIApplication,
28 | didReceiveRemoteNotification userInfo: [AnyHashable : Any]) {
29 | let dict = userInfo as? [String: AnyObject]
30 | let deepLink = DeepLinkOption.build(with: dict)
31 | applicationCoordinator.start(with: deepLink)
32 | }
33 |
34 | func application(_ application: UIApplication, continue userActivity: NSUserActivity,
35 | restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
36 | let deepLink = DeepLinkOption.build(with: userActivity)
37 | applicationCoordinator.start(with: deepLink)
38 | return true
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/ApplicationCoordinator/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/ApplicationCoordinator/Flows/Items flow/ItemsListController.swift:
--------------------------------------------------------------------------------
1 | final class ItemsListController: UIViewController, ItemsListView {
2 |
3 | //controller handler
4 | var onItemSelect: ((ItemList) -> ())?
5 | var onCreateItem: (() -> Void)?
6 |
7 | @IBAction func addItemButtonClicked(_ sender: UIBarButtonItem) { onCreateItem?() }
8 |
9 | @IBOutlet weak var tableView: UITableView!
10 | //mock datasource
11 | var items = (0...10).map { index in return ItemList(title: "Item № \(index)", subtitle: "Item descripton") }
12 | var authCheck = false
13 |
14 | override func viewDidLoad() {
15 | super.viewDidLoad()
16 |
17 | title = "Items"
18 | navigationItem.rightBarButtonItem = UIBarButtonItem(
19 | barButtonSystemItem: .add,
20 | target: self,
21 | action: #selector(ItemsListController.addItemButtonClicked(_:))
22 | )
23 | }
24 | }
25 |
26 | extension ItemsListController: UITableViewDelegate, UITableViewDataSource {
27 |
28 | func numberOfSections(in tableView: UITableView) -> Int {
29 | return 1
30 | }
31 |
32 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
33 | return items.count
34 | }
35 |
36 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
37 | let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
38 | let item = items[(indexPath as NSIndexPath).row]
39 | cell.textLabel?.text = item.title
40 | cell.detailTextLabel?.text = item.subtitle
41 | return cell
42 | }
43 |
44 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
45 | onItemSelect?(items[(indexPath as NSIndexPath).row])
46 | tableView.deselectRow(at: indexPath, animated: true)
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/ApplicationCoordinatorTests/Parent/BaseCoordinatorTest.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BaseCoordinatorTest.swift
3 | // ApplicationCoordinator
4 | //
5 | // Created by Andrey on 04.09.16.
6 | // Copyright © 2016 Andrey Panov. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import ApplicationCoordinator
11 |
12 | class BaseCoordinatorTest: XCTestCase {
13 |
14 | var coordinator: BaseCoordinator!
15 |
16 | override func setUp() {
17 | super.setUp()
18 |
19 | coordinator = BaseCoordinator()
20 | }
21 |
22 | override func tearDown() {
23 | coordinator = nil
24 | super.tearDown()
25 | }
26 |
27 | func testCoordinatorArrayInitializedOfEmptyArray() {
28 | XCTAssertTrue(coordinator.childCoordinators.isEmpty)
29 | }
30 |
31 | func testCoordinatorAddDependency() {
32 |
33 | coordinator.addDependency(coordinator)
34 | XCTAssertTrue(coordinator.childCoordinators.first is BaseCoordinator)
35 | XCTAssertTrue(coordinator.childCoordinators.count == 1)
36 | coordinator.addDependency(coordinator)
37 | XCTAssertTrue(coordinator.childCoordinators.count == 1, "Only unique reference could be added")
38 |
39 | let newCoordinator = BaseCoordinator()
40 | coordinator.addDependency(newCoordinator)
41 | XCTAssertTrue(coordinator.childCoordinators.count == 2)
42 | }
43 |
44 | func testCoordinatorRemoveDependency() {
45 |
46 | coordinator.addDependency(coordinator)
47 | XCTAssertTrue(coordinator.childCoordinators.first is BaseCoordinator)
48 | coordinator.removeDependency(coordinator)
49 | XCTAssertTrue(coordinator.childCoordinators.isEmpty)
50 | coordinator.removeDependency(coordinator)
51 | XCTAssertTrue(coordinator.childCoordinators.isEmpty, "If we try to remove removed referense, crush can't happend")
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/ApplicationCoordinator/Application/ApplicationCoordinator.swift:
--------------------------------------------------------------------------------
1 | fileprivate var onboardingWasShown = false
2 | fileprivate var isAutorized = false
3 |
4 | fileprivate enum LaunchInstructor {
5 | case main, auth, onboarding
6 |
7 | static func configure(
8 | tutorialWasShown: Bool = onboardingWasShown,
9 | isAutorized: Bool = isAutorized) -> LaunchInstructor {
10 |
11 | switch (tutorialWasShown, isAutorized) {
12 | case (true, false), (false, false): return .auth
13 | case (false, true): return .onboarding
14 | case (true, true): return .main
15 | }
16 | }
17 | }
18 |
19 | final class ApplicationCoordinator: BaseCoordinator {
20 |
21 | private let coordinatorFactory: CoordinatorFactory
22 | private let router: Router
23 |
24 | private var instructor: LaunchInstructor {
25 | return LaunchInstructor.configure()
26 | }
27 |
28 | init(router: Router, coordinatorFactory: CoordinatorFactory) {
29 | self.router = router
30 | self.coordinatorFactory = coordinatorFactory
31 | }
32 |
33 | override func start(with option: DeepLinkOption?) {
34 | //start with deepLink
35 | if let option = option {
36 | switch option {
37 | case .onboarding: runOnboardingFlow()
38 | case .signUp: runAuthFlow()
39 | default: childCoordinators.forEach { coordinator in
40 | coordinator.start(with: option)
41 | }
42 | }
43 | // default start
44 | } else {
45 | switch instructor {
46 | case .onboarding: runOnboardingFlow()
47 | case .auth: runAuthFlow()
48 | case .main: runMainFlow()
49 | }
50 | }
51 | }
52 |
53 | private func runAuthFlow() {
54 | let coordinator = coordinatorFactory.makeAuthCoordinatorBox(router: router)
55 | coordinator.finishFlow = { [weak self, weak coordinator] in
56 | isAutorized = true
57 | self?.start()
58 | self?.removeDependency(coordinator)
59 | }
60 | addDependency(coordinator)
61 | coordinator.start()
62 | }
63 |
64 | private func runOnboardingFlow() {
65 | let coordinator = coordinatorFactory.makeOnboardingCoordinator(router: router)
66 | coordinator.finishFlow = { [weak self, weak coordinator] in
67 | onboardingWasShown = true
68 | self?.start()
69 | self?.removeDependency(coordinator)
70 | }
71 | addDependency(coordinator)
72 | coordinator.start()
73 | }
74 |
75 | private func runMainFlow() {
76 | let (coordinator, module) = coordinatorFactory.makeTabbarCoordinator()
77 | addDependency(coordinator)
78 | router.setRootModule(module, hideBar: true)
79 | coordinator.start()
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/ApplicationCoordinator/Factories/CoordinatorFactoryImp.swift:
--------------------------------------------------------------------------------
1 | final class CoordinatorFactoryImp: CoordinatorFactory {
2 |
3 | func makeTabbarCoordinator() -> (configurator: Coordinator, toPresent: Presentable?) {
4 | let controller = TabbarController.controllerFromStoryboard(.main)
5 | let coordinator = TabbarCoordinator(tabbarView: controller, coordinatorFactory: CoordinatorFactoryImp())
6 | return (coordinator, controller)
7 | }
8 |
9 | func makeAuthCoordinatorBox(router: Router) -> Coordinator & AuthCoordinatorOutput {
10 |
11 | let coordinator = AuthCoordinator(router: router, factory: ModuleFactoryImp())
12 | return coordinator
13 | }
14 |
15 | func makeItemCoordinator() -> Coordinator {
16 | return makeItemCoordinator(navController: nil)
17 | }
18 |
19 | func makeOnboardingCoordinator(router: Router) -> Coordinator & OnboardingCoordinatorOutput {
20 | return OnboardingCoordinator(with: ModuleFactoryImp(), router: router)
21 | }
22 |
23 | func makeItemCoordinator(navController: UINavigationController?) -> Coordinator {
24 | let coordinator = ItemCoordinator(
25 | router: router(navController),
26 | factory: ModuleFactoryImp(),
27 | coordinatorFactory: CoordinatorFactoryImp()
28 | )
29 | return coordinator
30 | }
31 |
32 | func makeSettingsCoordinator() -> Coordinator {
33 | return makeSettingsCoordinator(navController: nil)
34 | }
35 |
36 | func makeSettingsCoordinator(navController: UINavigationController? = nil) -> Coordinator {
37 | let coordinator = SettingsCoordinator(router: router(navController), factory: ModuleFactoryImp())
38 | return coordinator
39 | }
40 |
41 | func makeItemCreationCoordinatorBox() ->
42 | (configurator: Coordinator & ItemCreateCoordinatorOutput,
43 | toPresent: Presentable?) {
44 |
45 | return makeItemCreationCoordinatorBox(navController: navigationController(nil))
46 | }
47 | func makeItemCreationCoordinatorBox(navController: UINavigationController?) ->
48 | (configurator: Coordinator & ItemCreateCoordinatorOutput,
49 | toPresent: Presentable?) {
50 |
51 | let router = self.router(navController)
52 | let coordinator = ItemCreateCoordinator(router: router, factory: ModuleFactoryImp())
53 | return (coordinator, router)
54 | }
55 |
56 | private func router(_ navController: UINavigationController?) -> Router {
57 | return RouterImp(rootController: navigationController(navController))
58 | }
59 |
60 | private func navigationController(_ navController: UINavigationController?) -> UINavigationController {
61 | if let navController = navController { return navController }
62 | else { return UINavigationController.controllerFromStoryboard(.main) }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/ApplicationCoordinatorTests/ItemsFlow/ItemCoordinatorTest.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ItemCoordinatorTest.swift
3 | // ApplicationCoordinator
4 | //
5 | // Created by Andrey on 18.09.16.
6 | // Copyright © 2016 Andrey Panov. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import ApplicationCoordinator
11 |
12 | class ItemCoordinatorTest: XCTestCase {
13 |
14 | private var coordinator: Coordinator!
15 | private var router: RouterMock!
16 |
17 | private var itemListOutput: ItemsListView!
18 | private var itemDetailOutput: ItemDetailView!
19 |
20 | override func setUp() {
21 | super.setUp()
22 |
23 | router = RouterMockImp()
24 | let itemListController = ItemsListController.controllerFromStoryboard(.items)
25 | let itemDetailController = ItemDetailController.controllerFromStoryboard(.items)
26 | let factory = ItemModuleFactoryMock(itemListController: itemListController, itemDetailCntroller: itemDetailController)
27 | coordinator = ItemCoordinator(router: router,
28 | factory: factory,
29 | coordinatorFactory: CoordinatorFactoryImp())
30 | itemListOutput = itemListController
31 | itemDetailOutput = itemDetailController
32 |
33 | }
34 |
35 | override func tearDown() {
36 | coordinator = nil
37 | router = nil
38 | itemListOutput = nil
39 | itemDetailOutput = nil
40 |
41 | super.tearDown()
42 | }
43 |
44 | func testStart() {
45 |
46 | coordinator.start()
47 | // login controller must be in navigation stack
48 | XCTAssertTrue(router.navigationStack.first is ItemsListController)
49 | XCTAssertTrue(router.navigationStack.count == 1)
50 | }
51 |
52 | func testShowItemDetail() {
53 |
54 | coordinator.start()
55 | itemListOutput.onItemSelect!(ItemList(title: "", subtitle: ""))
56 | XCTAssertTrue(router.navigationStack.last is ItemDetailController)
57 | XCTAssertTrue(router.navigationStack.count == 2)
58 |
59 | }
60 | }
61 |
62 | final class ItemModuleFactoryMock: ItemModuleFactory {
63 |
64 | private let itemListController: ItemsListController
65 | private let itemDetailCntroller: ItemDetailController
66 |
67 | init(itemListController: ItemsListController,
68 | itemDetailCntroller: ItemDetailController) {
69 |
70 | self.itemListController = itemListController
71 | self.itemDetailCntroller = itemDetailCntroller
72 | }
73 |
74 | func makeItemsOutput() -> ItemsListView {
75 | return itemListController
76 | }
77 |
78 | func makeItemDetailOutput(item: ItemList) -> ItemDetailView {
79 | return itemDetailCntroller
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/ApplicationCoordinatorTests/Router/RouterTest.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RouterTest.swift
3 | // ApplicationCoordinator
4 | //
5 | // Created by Andrey on 02.09.16.
6 | // Copyright © 2016 Andrey Panov. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import ApplicationCoordinator
11 |
12 | class RouterTest: XCTestCase {
13 |
14 | private var router: RouterMock!
15 |
16 | private var firstController: UIViewController!
17 | private var secondController: UIViewController!
18 | private var thirdController: UIViewController!
19 |
20 | override func setUp() {
21 | super.setUp()
22 |
23 | router = RouterMockImp()
24 | firstController = ItemsListController.controllerFromStoryboard(.items)
25 | secondController = ItemDetailController.controllerFromStoryboard(.items)
26 | thirdController = SettingsController.controllerFromStoryboard(.settings)
27 | }
28 |
29 | override func tearDown() {
30 |
31 | router = nil
32 | firstController = nil
33 | secondController = nil
34 | thirdController = nil
35 |
36 | super.tearDown()
37 | }
38 |
39 | func testRouterSetRootModule() {
40 |
41 | router.setRootModule(firstController)
42 | XCTAssertTrue(router.navigationStack.first is ItemsListController)
43 | }
44 |
45 | func testRouterPushViewModule() {
46 |
47 | router.setRootModule(firstController)
48 | XCTAssertTrue(router.navigationStack.last is ItemsListController)
49 | router.push(secondController)
50 | XCTAssertTrue(router.navigationStack.last is ItemDetailController)
51 | }
52 |
53 | func testRouterPopViewModule() {
54 |
55 | router.setRootModule(firstController)
56 | XCTAssertTrue(router.navigationStack.last is ItemsListController)
57 | router.push(secondController)
58 | XCTAssertTrue(router.navigationStack.last is ItemDetailController)
59 |
60 | router.popModule()
61 | XCTAssertTrue(router.navigationStack.last is ItemsListController)
62 | }
63 |
64 | func testRouterPopToRootViewModule() {
65 |
66 | router.setRootModule(firstController)
67 | XCTAssertTrue(router.navigationStack.last is ItemsListController)
68 | router.push(secondController)
69 | XCTAssertTrue(router.navigationStack.last is ItemDetailController)
70 | router.push(thirdController)
71 | XCTAssertTrue(router.navigationStack.last is SettingsController)
72 |
73 | router.popToRootModule(animated: false)
74 | XCTAssertTrue(router.navigationStack.last is ItemsListController)
75 | }
76 |
77 | func testPresentViewModule() {
78 | router.present(secondController)
79 | XCTAssertTrue(router.presented is ItemDetailController)
80 | }
81 |
82 | func testDismissViewModule() {
83 |
84 | router.present(secondController)
85 | XCTAssertTrue(router.presented is ItemDetailController)
86 | router.dismissModule()
87 | XCTAssertTrue(router.presented == nil)
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/ApplicationCoordinator/Presenters/RouterImp.swift:
--------------------------------------------------------------------------------
1 | final class RouterImp: NSObject, Router {
2 |
3 | private weak var rootController: UINavigationController?
4 | private var completions: [UIViewController : () -> Void]
5 |
6 | init(rootController: UINavigationController) {
7 | self.rootController = rootController
8 | completions = [:]
9 | }
10 |
11 | func toPresent() -> UIViewController? {
12 | return rootController
13 | }
14 |
15 | func present(_ module: Presentable?) {
16 | present(module, animated: true)
17 | }
18 |
19 | func present(_ module: Presentable?, animated: Bool) {
20 | guard let controller = module?.toPresent() else { return }
21 | rootController?.present(controller, animated: animated, completion: nil)
22 | }
23 |
24 | func dismissModule() {
25 | dismissModule(animated: true, completion: nil)
26 | }
27 |
28 | func dismissModule(animated: Bool, completion: (() -> Void)?) {
29 | rootController?.dismiss(animated: animated, completion: completion)
30 | }
31 |
32 | func push(_ module: Presentable?) {
33 | push(module, animated: true)
34 | }
35 |
36 | func push(_ module: Presentable?, hideBottomBar: Bool) {
37 | push(module, animated: true, hideBottomBar: hideBottomBar, completion: nil)
38 | }
39 |
40 | func push(_ module: Presentable?, animated: Bool) {
41 | push(module, animated: animated, completion: nil)
42 | }
43 |
44 | func push(_ module: Presentable?, animated: Bool, completion: (() -> Void)?) {
45 | push(module, animated: animated, hideBottomBar: false, completion: completion)
46 | }
47 |
48 | func push(_ module: Presentable?, animated: Bool, hideBottomBar: Bool, completion: (() -> Void)?) {
49 | guard
50 | let controller = module?.toPresent(),
51 | (controller is UINavigationController == false)
52 | else { assertionFailure("Deprecated push UINavigationController."); return }
53 |
54 | if let completion = completion {
55 | completions[controller] = completion
56 | }
57 | controller.hidesBottomBarWhenPushed = hideBottomBar
58 | rootController?.pushViewController(controller, animated: animated)
59 | }
60 |
61 | func popModule() {
62 | popModule(animated: true)
63 | }
64 |
65 | func popModule(animated: Bool) {
66 | if let controller = rootController?.popViewController(animated: animated) {
67 | runCompletion(for: controller)
68 | }
69 | }
70 |
71 | func setRootModule(_ module: Presentable?) {
72 | setRootModule(module, hideBar: false)
73 | }
74 |
75 | func setRootModule(_ module: Presentable?, hideBar: Bool) {
76 | guard let controller = module?.toPresent() else { return }
77 | rootController?.setViewControllers([controller], animated: false)
78 | rootController?.isNavigationBarHidden = hideBar
79 | }
80 |
81 | func popToRootModule(animated: Bool) {
82 | if let controllers = rootController?.popToRootViewController(animated: animated) {
83 | controllers.forEach { controller in
84 | runCompletion(for: controller)
85 | }
86 | }
87 | }
88 |
89 | private func runCompletion(for controller: UIViewController) {
90 | guard let completion = completions[controller] else { return }
91 | completion()
92 | completions.removeValue(forKey: controller)
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/ApplicationCoordinatorTests/LoginFlow/AuthCoordinatorTest.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AuthCoordinatorTest.swift
3 | // ApplicationCoordinator
4 | //
5 | // Created by Andrey on 04.09.16.
6 | // Copyright © 2016 Andrey Panov. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import ApplicationCoordinator
11 |
12 | class AuthCoordinatorTest: XCTestCase {
13 |
14 | private var coordinator: Coordinator!
15 | private var router: RouterMock!
16 |
17 | private var loginOutput: LoginView!
18 | private var signUpOutput: SignUpView!
19 | private var signUpController: SignUpController!
20 | private var termsOutput: TermsView!
21 |
22 | override func setUp() {
23 | super.setUp()
24 |
25 | router = RouterMockImp()
26 | let loginController = LoginController.controllerFromStoryboard(.auth)
27 | signUpController = SignUpController.controllerFromStoryboard(.auth)
28 | signUpController.view.isHidden = false
29 | let termsController = TermsController.controllerFromStoryboard(.auth)
30 | let factory = AuthModuleFactoryMock(loginController: loginController,
31 | signUpController: signUpController,
32 | termsController: termsController)
33 | coordinator = AuthCoordinator(router: router, factory: factory)
34 |
35 | loginOutput = loginController
36 | signUpOutput = signUpController
37 | termsOutput = termsController
38 | }
39 |
40 | override func tearDown() {
41 | coordinator = nil
42 | router = nil
43 | loginOutput = nil
44 | signUpOutput = nil
45 | termsOutput = nil
46 |
47 | super.tearDown()
48 | }
49 |
50 | func testStart() {
51 |
52 | coordinator.start()
53 | // showLogin() must call
54 | XCTAssertTrue(router.navigationStack.first is LoginController)
55 | XCTAssertTrue(router.navigationStack.count == 1)
56 | }
57 |
58 | func testShowSignUp() {
59 |
60 | coordinator.start()
61 | // onSignUpButtonTap event
62 | loginOutput.onSignUpButtonTap!()
63 | XCTAssertTrue(router.navigationStack.last is SignUpController)
64 | XCTAssertTrue(router.navigationStack.count == 2)
65 | }
66 |
67 | func testShowTerms() {
68 |
69 | //show login controller
70 | coordinator.start()
71 | // show signUp controller
72 | loginOutput.onSignUpButtonTap!()
73 | //show terms controller
74 | signUpOutput.onTermsButtonTap!()
75 | XCTAssertTrue(router.navigationStack.last is TermsController)
76 | XCTAssertTrue(router.navigationStack.count == 3)
77 | }
78 | }
79 |
80 | final class AuthModuleFactoryMock: AuthModuleFactory {
81 |
82 | private let loginController: LoginController
83 | private let signUpController: SignUpController
84 | private let termsController: TermsController
85 |
86 | init(loginController: LoginController,
87 | signUpController: SignUpController,
88 | termsController: TermsController) {
89 |
90 | self.loginController = loginController
91 | self.signUpController = signUpController
92 | self.termsController = termsController
93 | }
94 |
95 | func makeLoginOutput() -> LoginView {
96 | return loginController
97 | }
98 |
99 | func makeSignUpHandler() -> SignUpView {
100 | return signUpController
101 | }
102 |
103 | func makeTermsOutput() -> TermsView {
104 | return termsController
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ApplicationCoordinator
2 | A lot of developers need to change navigation flow frequently, because it depends on business tasks. And they spend a huge amount of time for re-writing code. In this approach, I demonstrate our implementation of Coordinators, the creation of a protocol-oriented, testable architecture written on pure Swift without the downcast and, also to avoid the violation of the S.O.L.I.D. principles.
3 |
4 | Based on the post about Application Coordinators [khanlou.com](http://khanlou.com/2015/10/coordinators-redux/) and Application Controller pattern description [martinfowler.com](http://martinfowler.com/eaaCatalog/applicationController.html).
5 |
6 |
7 | ### Coordinators Essential tutorial. Part I [medium.com](https://medium.com/blacklane-engineering/coordinators-essential-tutorial-part-i-376c836e9ba7)
8 |
9 | ### Coordinators Essential tutorial. Part II [medium.com](https://medium.com/@panovdev/coordinators-essential-tutorial-part-ii-b5ab3eb4a74)
10 |
11 |
12 | Example provides very basic structure with 6 controllers and 5 coordinators with mock data and logic.
13 | 
14 |
15 | I used a protocol for coordinators in this example:
16 | ```swift
17 | protocol Coordinator: class {
18 | func start()
19 | func start(with option: DeepLinkOption?)
20 | }
21 | ```
22 | All flow controllers have a protocols (we need to configure blocks and handle callbacks in coordinators):
23 | ```swift
24 | protocol ItemsListView: BaseView {
25 | var authNeed: (() -> ())? { get set }
26 | var onItemSelect: (ItemList -> ())? { get set }
27 | var onCreateButtonTap: (() -> ())? { get set }
28 | }
29 | ```
30 | In this example I use factories for creating coordinators and controllers (we can mock them in tests).
31 | ```swift
32 | protocol CoordinatorFactory {
33 | func makeItemCoordinator(navController navController: UINavigationController?) -> Coordinator
34 | func makeItemCoordinator() -> Coordinator
35 |
36 | func makeItemCreationCoordinatorBox(navController: UINavigationController?) ->
37 | (configurator: Coordinator & ItemCreateCoordinatorOutput,
38 | toPresent: Presentable?)
39 | }
40 | ```
41 | The base coordinator stores dependencies of child coordinators
42 | ```swift
43 | class BaseCoordinator: Coordinator {
44 |
45 | var childCoordinators: [Coordinator] = []
46 |
47 | func start() { }
48 | func start(with option: DeepLinkOption?) { }
49 |
50 | // add only unique object
51 | func addDependency(_ coordinator: Coordinator) {
52 |
53 | for element in childCoordinators {
54 | if element === coordinator { return }
55 | }
56 | childCoordinators.append(coordinator)
57 | }
58 |
59 | func removeDependency(_ coordinator: Coordinator?) {
60 | guard
61 | childCoordinators.isEmpty == false,
62 | let coordinator = coordinator
63 | else { return }
64 |
65 | for (index, element) in childCoordinators.enumerated() {
66 | if element === coordinator {
67 | childCoordinators.remove(at: index)
68 | break
69 | }
70 | }
71 | }
72 | }
73 | ```
74 | AppDelegate store lazy reference for the Application Coordinator
75 | ```swift
76 | var rootController: UINavigationController {
77 | return self.window!.rootViewController as! UINavigationController
78 | }
79 |
80 | private lazy var applicationCoordinator: Coordinator = self.makeCoordinator()
81 |
82 | func application(_ application: UIApplication,
83 | didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
84 | let notification = launchOptions?[.remoteNotification] as? [String: AnyObject]
85 | let deepLink = DeepLinkOption.build(with: notification)
86 | applicationCoordinator.start(with: deepLink)
87 | return true
88 | }
89 |
90 | private func makeCoordinator() -> Coordinator {
91 | return ApplicationCoordinator(
92 | router: RouterImp(rootController: self.rootController),
93 | coordinatorFactory: CoordinatorFactoryImp()
94 | )
95 | }
96 | ```
97 |
--------------------------------------------------------------------------------
/ApplicationCoordinatorTests/Router/RouterMock.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RouterMock.swift
3 | // ApplicationCoordinator
4 | //
5 | // Created by Andrey on 02.09.16.
6 | // Copyright © 2016 Andrey Panov. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | @testable import ApplicationCoordinator
11 |
12 | protocol RouterMock: Router {
13 | var navigationStack: [UIViewController] {get}
14 | var presented: UIViewController? {get}
15 | }
16 |
17 | final class RouterMockImp: RouterMock {
18 |
19 | // in test cases router store the rootController referense
20 | private(set) var navigationStack: [UIViewController] = []
21 | private(set) var presented: UIViewController?
22 | private var completions: [UIViewController : () -> Void] = [:]
23 |
24 | func toPresent() -> UIViewController? {
25 | return nil
26 | }
27 |
28 | //all of the actions without animation
29 | func present(_ module: Presentable?) {
30 | present(module, animated: false)
31 | }
32 | func present(_ module: Presentable?, animated: Bool) {
33 | guard let controller = module?.toPresent() else { return }
34 | presented = controller
35 | }
36 |
37 | func push(_ module: Presentable?) {
38 | push(module, animated: false)
39 | }
40 |
41 | func push(_ module: Presentable?, animated: Bool) {
42 | push(module, animated: animated, completion: nil)
43 | }
44 |
45 | func push(_ module: Presentable?, hideBottomBar: Bool) {
46 | guard
47 | let controller = module?.toPresent(),
48 | (controller is UINavigationController == false)
49 | else { assertionFailure("Deprecated push UINavigationController."); return }
50 |
51 | controller.hidesBottomBarWhenPushed = hideBottomBar
52 |
53 | push(module, animated: false)
54 | }
55 |
56 | func push(_ module: Presentable?, animated: Bool, hideBottomBar: Bool, completion: (() -> Void)?) {
57 |
58 | guard
59 | let controller = module?.toPresent(),
60 | (controller is UINavigationController == false)
61 | else { assertionFailure("Deprecated push UINavigationController."); return }
62 |
63 | controller.hidesBottomBarWhenPushed = hideBottomBar
64 | navigationStack.append(controller)
65 | }
66 |
67 | func push(_ module: Presentable?, animated: Bool, completion: (() -> Void)?) {
68 | guard
69 | let controller = module?.toPresent(),
70 | (controller is UINavigationController == false)
71 | else { assertionFailure("Deprecated push UINavigationController."); return }
72 | navigationStack.append(controller)
73 | }
74 |
75 | func popModule() {
76 | popModule(animated: false)
77 | }
78 |
79 | func popModule(animated: Bool) {
80 | let controller = navigationStack.removeLast()
81 | runCompletion(for: controller)
82 | }
83 |
84 | func dismissModule() {
85 | dismissModule(animated: false, completion: nil)
86 | }
87 |
88 | func dismissModule(animated: Bool, completion: (() -> Void)?) {
89 | presented = nil
90 | }
91 |
92 | func setRootModule(_ module: Presentable?) {
93 | guard let controller = module?.toPresent() else { return }
94 | navigationStack.append(controller)
95 | }
96 |
97 | func setRootModule(_ module: Presentable?, hideBar: Bool) {
98 | assertionFailure("This method is not used.")
99 | }
100 |
101 | func popToRootModule(animated: Bool) {
102 | guard let first = navigationStack.first else { return }
103 |
104 | navigationStack.forEach { controller in
105 | runCompletion(for: controller)
106 | }
107 | navigationStack = [first]
108 | }
109 |
110 | private func runCompletion(for controller: UIViewController) {
111 | guard let completion = completions[controller] else { return }
112 | completion()
113 | completions.removeValue(forKey: controller)
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/ApplicationCoordinator/Storyboards/Create.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/ApplicationCoordinator/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
--------------------------------------------------------------------------------
/ApplicationCoordinator/Storyboards/Settings.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
38 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
--------------------------------------------------------------------------------
/ApplicationCoordinator/Storyboards/Onboarding.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 |
30 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
69 |
76 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
--------------------------------------------------------------------------------
/ApplicationCoordinator/Storyboards/Items.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
38 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 | «So what is a coordinator? A coordinator is an object that bosses view controllers around. Taking all of the driving logic out of your view controllers, and moving that stuff one layer up is gonna make your life a lot more awesome.» –Soroush Khanlou
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
--------------------------------------------------------------------------------
/ApplicationCoordinator/Storyboards/Auth.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
53 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
108 |
118 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
--------------------------------------------------------------------------------
/ApplicationCoordinator.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 0E3CCF991CCBBB670022E830 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3CCF7D1CCBBB670022E830 /* AppDelegate.swift */; };
11 | 0E3CCF9A1CCBBB670022E830 /* ApplicationCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3CCF7E1CCBBB670022E830 /* ApplicationCoordinator.swift */; };
12 | 0E3CCF9D1CCBBB670022E830 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0E3CCF821CCBBB670022E830 /* LaunchScreen.storyboard */; };
13 | 0E3CCF9E1CCBBB670022E830 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0E3CCF841CCBBB670022E830 /* Main.storyboard */; };
14 | 0E3CCFA01CCBBB670022E830 /* UIViewControllerExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3CCF881CCBBB670022E830 /* UIViewControllerExtension.swift */; };
15 | 0E3CCFA81CCBBB670022E830 /* Auth.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0E3CCF971CCBBB670022E830 /* Auth.storyboard */; };
16 | 0E3CCFA91CCBBB670022E830 /* Items.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0E3CCF981CCBBB670022E830 /* Items.storyboard */; };
17 | 0E3CCFB11CCBBC760022E830 /* Create.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0E3CCFAF1CCBBC760022E830 /* Create.storyboard */; };
18 | 0E3CCFB21CCBBC760022E830 /* Settings.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0E3CCFB01CCBBC760022E830 /* Settings.storyboard */; };
19 | 0E3CCFB41CCBBCA20022E830 /* ItemCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3CCFB31CCBBCA20022E830 /* ItemCoordinator.swift */; };
20 | 0E3CCFBA1CCBBFEF0022E830 /* LoginController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3CCFB91CCBBFEF0022E830 /* LoginController.swift */; };
21 | 0E3CCFBC1CCBC0210022E830 /* AuthCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3CCFBB1CCBC0210022E830 /* AuthCoordinator.swift */; };
22 | 0E3CCFBE1CCBC04B0022E830 /* SettingsCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3CCFBD1CCBC04B0022E830 /* SettingsCoordinator.swift */; };
23 | 0E3CCFC01CCBC0600022E830 /* SettingsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3CCFBF1CCBC0600022E830 /* SettingsController.swift */; };
24 | 0E3CCFC21CCBC0AA0022E830 /* ItemCreateCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3CCFC11CCBC0AA0022E830 /* ItemCreateCoordinator.swift */; };
25 | 0E3CCFC71CCBC6740022E830 /* ItemList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3CCFC61CCBC6740022E830 /* ItemList.swift */; };
26 | 0E3CCFC91CCBE87F0022E830 /* SignUpController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3CCFC81CCBE87F0022E830 /* SignUpController.swift */; };
27 | 0E3CCFCB1CCBFB150022E830 /* Setting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3CCFCA1CCBFB150022E830 /* Setting.swift */; };
28 | 0E6A42BE1CF4DE62005746EC /* CoordinatorFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E6A42BD1CF4DE62005746EC /* CoordinatorFactory.swift */; };
29 | 0E6A42C01CF4E06C005746EC /* ItemModuleFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E6A42BF1CF4E06C005746EC /* ItemModuleFactory.swift */; };
30 | 0E6A42C21CF4E29F005746EC /* ItemCreateModuleFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E6A42C11CF4E29F005746EC /* ItemCreateModuleFactory.swift */; };
31 | 0E6A42C41CF4E300005746EC /* AuthModuleFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E6A42C31CF4E300005746EC /* AuthModuleFactory.swift */; };
32 | 0E6A42C61CF4E45F005746EC /* SettingsModuleFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E6A42C51CF4E45F005746EC /* SettingsModuleFactory.swift */; };
33 | 0E6A42CA1CF4E81E005746EC /* ItemCreateCoordinatorOutput.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E6A42C91CF4E81E005746EC /* ItemCreateCoordinatorOutput.swift */; };
34 | 0E6A42CD1CF4E862005746EC /* AuthCoordinatorOutput.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E6A42CC1CF4E862005746EC /* AuthCoordinatorOutput.swift */; };
35 | 0E6A42D21CF627CC005746EC /* TermsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E6A42D11CF627CC005746EC /* TermsController.swift */; };
36 | 0E6A42D41CF62EA2005746EC /* TermsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E6A42D31CF62EA2005746EC /* TermsView.swift */; };
37 | 0EDCA2F51CEE365300A6E385 /* Router.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EDCA2F41CEE365300A6E385 /* Router.swift */; };
38 | 7712177D1D7C89EA0070CDF9 /* BaseCoordinatorTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7712177C1D7C89EA0070CDF9 /* BaseCoordinatorTest.swift */; };
39 | 7712177F1D7C91670070CDF9 /* SettingsCoordinatorTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7712177E1D7C91670070CDF9 /* SettingsCoordinatorTest.swift */; };
40 | 771217811D7C94290070CDF9 /* AuthCoordinatorTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 771217801D7C94290070CDF9 /* AuthCoordinatorTest.swift */; };
41 | 77B3E7911D8E90550095527A /* ItemCoordinatorTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77B3E7901D8E90550095527A /* ItemCoordinatorTest.swift */; };
42 | 77E054CB1D7937C900147DAE /* RouterTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77E054CA1D7937C900147DAE /* RouterTest.swift */; };
43 | 77E054CD1D79391600147DAE /* RouterMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77E054CC1D79391600147DAE /* RouterMock.swift */; };
44 | AD1BFD591E2930C40022C865 /* DeepLinkOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD1BFD581E2930C40022C865 /* DeepLinkOption.swift */; };
45 | AD2BFC0B1E75B2730059C854 /* OnboardingModuleFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD2BFC0A1E75B2730059C854 /* OnboardingModuleFactory.swift */; };
46 | AD2BFC101E75B7FE0059C854 /* OnboardingController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD2BFC0D1E75B7FE0059C854 /* OnboardingController.swift */; };
47 | AD2BFC111E75B7FE0059C854 /* PageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD2BFC0E1E75B7FE0059C854 /* PageViewController.swift */; };
48 | AD2BFC121E75B7FE0059C854 /* StepController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD2BFC0F1E75B7FE0059C854 /* StepController.swift */; };
49 | AD2BFC141E75B81B0059C854 /* OnboardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD2BFC131E75B81B0059C854 /* OnboardingView.swift */; };
50 | AD2BFC161E75B95F0059C854 /* OnboardingCoordinatorOutput.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD2BFC151E75B95F0059C854 /* OnboardingCoordinatorOutput.swift */; };
51 | ADE320CE1E47941B0087146A /* OnboardingCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = ADE320CD1E47941B0087146A /* OnboardingCoordinator.swift */; };
52 | ADE320D01E47943B0087146A /* Onboarding.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = ADE320CF1E47943B0087146A /* Onboarding.storyboard */; };
53 | ADE320F71E4796D90087146A /* TabbarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = ADE320F41E4796D90087146A /* TabbarController.swift */; };
54 | ADE320F81E4796D90087146A /* TabbarCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = ADE320F51E4796D90087146A /* TabbarCoordinator.swift */; };
55 | ADE320F91E4796D90087146A /* TabbarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = ADE320F61E4796D90087146A /* TabbarView.swift */; };
56 | D5311AAF1CF8566D00777B87 /* ItemCreateController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5311AAD1CF8566D00777B87 /* ItemCreateController.swift */; };
57 | D5311AB01CF8566D00777B87 /* ItemCreateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5311AAE1CF8566D00777B87 /* ItemCreateView.swift */; };
58 | D53C00BA1D1BBADA00573E71 /* Presentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D53C00B91D1BBADA00573E71 /* Presentable.swift */; };
59 | D541E7641CD9126F004C38B3 /* Coordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D541E7631CD9126F004C38B3 /* Coordinator.swift */; };
60 | D541E7661CD9128B004C38B3 /* BaseView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D541E7651CD9128B004C38B3 /* BaseView.swift */; };
61 | D541E76A1CD91304004C38B3 /* RouterImp.swift in Sources */ = {isa = PBXBuildFile; fileRef = D541E7691CD91304004C38B3 /* RouterImp.swift */; };
62 | D545E4A21CD1FF40002B6F8F /* BaseCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D545E4A11CD1FF40002B6F8F /* BaseCoordinator.swift */; };
63 | D5505CC71CDF67F60012CBA6 /* UIViewExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5505CC61CDF67F60012CBA6 /* UIViewExtensions.swift */; };
64 | D5505CC91CDF68260012CBA6 /* NSObjectExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5505CC81CDF68260012CBA6 /* NSObjectExtensions.swift */; };
65 | D5505CCB1CDF68D10012CBA6 /* StoryboardsEnum.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5505CCA1CDF68D10012CBA6 /* StoryboardsEnum.swift */; };
66 | D5FDC84E1CEB3579001C3DF3 /* LoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5FDC84D1CEB3579001C3DF3 /* LoginView.swift */; };
67 | D5FDC8501CEB3AE0001C3DF3 /* SignUpView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5FDC84F1CEB3AE0001C3DF3 /* SignUpView.swift */; };
68 | D5FDC8541CEB56A8001C3DF3 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5FDC8531CEB56A8001C3DF3 /* SettingsView.swift */; };
69 | D5FE332A1D071ED900CE38B1 /* ModuleFactoryImp.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5FE33261D071ED900CE38B1 /* ModuleFactoryImp.swift */; };
70 | D5FE332F1D071F4100CE38B1 /* CoordinatorFactoryImp.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5FE332E1D071F4100CE38B1 /* CoordinatorFactoryImp.swift */; };
71 | D5FE33341D071F6300CE38B1 /* ItemDetailController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5FE33301D071F6300CE38B1 /* ItemDetailController.swift */; };
72 | D5FE33351D071F6300CE38B1 /* ItemDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5FE33311D071F6300CE38B1 /* ItemDetailView.swift */; };
73 | D5FE33361D071F6300CE38B1 /* ItemsListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5FE33321D071F6300CE38B1 /* ItemsListView.swift */; };
74 | D5FE33371D071F6300CE38B1 /* ItemsListController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5FE33331D071F6300CE38B1 /* ItemsListController.swift */; };
75 | D5FE33431D0720E500CE38B1 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D5FE33421D0720E500CE38B1 /* Images.xcassets */; };
76 | /* End PBXBuildFile section */
77 |
78 | /* Begin PBXContainerItemProxy section */
79 | 77E054B51D79312700147DAE /* PBXContainerItemProxy */ = {
80 | isa = PBXContainerItemProxy;
81 | containerPortal = D50F9EE71C7A525E00C5A59F /* Project object */;
82 | proxyType = 1;
83 | remoteGlobalIDString = D50F9EEE1C7A525E00C5A59F;
84 | remoteInfo = ApplicationCoordinator;
85 | };
86 | /* End PBXContainerItemProxy section */
87 |
88 | /* Begin PBXFileReference section */
89 | 0E3CCF7D1CCBBB670022E830 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = AppDelegate.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
90 | 0E3CCF7E1CCBBB670022E830 /* ApplicationCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = ApplicationCoordinator.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
91 | 0E3CCF831CCBBB670022E830 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = LaunchScreen.storyboard; sourceTree = ""; };
92 | 0E3CCF851CCBBB670022E830 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Main.storyboard; sourceTree = ""; };
93 | 0E3CCF881CCBBB670022E830 /* UIViewControllerExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIViewControllerExtension.swift; sourceTree = ""; };
94 | 0E3CCF971CCBBB670022E830 /* Auth.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Auth.storyboard; sourceTree = ""; };
95 | 0E3CCF981CCBBB670022E830 /* Items.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Items.storyboard; sourceTree = ""; };
96 | 0E3CCFAF1CCBBC760022E830 /* Create.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Create.storyboard; sourceTree = ""; };
97 | 0E3CCFB01CCBBC760022E830 /* Settings.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Settings.storyboard; sourceTree = ""; };
98 | 0E3CCFB31CCBBCA20022E830 /* ItemCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = ItemCoordinator.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
99 | 0E3CCFB91CCBBFEF0022E830 /* LoginController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = LoginController.swift; path = ../LoginController.swift; sourceTree = ""; };
100 | 0E3CCFBB1CCBC0210022E830 /* AuthCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = AuthCoordinator.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
101 | 0E3CCFBD1CCBC04B0022E830 /* SettingsCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = SettingsCoordinator.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
102 | 0E3CCFBF1CCBC0600022E830 /* SettingsController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = SettingsController.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
103 | 0E3CCFC11CCBC0AA0022E830 /* ItemCreateCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = ItemCreateCoordinator.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
104 | 0E3CCFC61CCBC6740022E830 /* ItemList.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ItemList.swift; sourceTree = ""; };
105 | 0E3CCFC81CCBE87F0022E830 /* SignUpController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SignUpController.swift; path = ../SignUpController.swift; sourceTree = ""; };
106 | 0E3CCFCA1CCBFB150022E830 /* Setting.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Setting.swift; sourceTree = ""; };
107 | 0E6A42BD1CF4DE62005746EC /* CoordinatorFactory.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = CoordinatorFactory.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
108 | 0E6A42BF1CF4E06C005746EC /* ItemModuleFactory.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = ItemModuleFactory.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
109 | 0E6A42C11CF4E29F005746EC /* ItemCreateModuleFactory.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = ItemCreateModuleFactory.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
110 | 0E6A42C31CF4E300005746EC /* AuthModuleFactory.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = AuthModuleFactory.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
111 | 0E6A42C51CF4E45F005746EC /* SettingsModuleFactory.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = SettingsModuleFactory.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
112 | 0E6A42C91CF4E81E005746EC /* ItemCreateCoordinatorOutput.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ItemCreateCoordinatorOutput.swift; sourceTree = ""; };
113 | 0E6A42CC1CF4E862005746EC /* AuthCoordinatorOutput.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthCoordinatorOutput.swift; sourceTree = ""; };
114 | 0E6A42D11CF627CC005746EC /* TermsController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TermsController.swift; sourceTree = ""; };
115 | 0E6A42D31CF62EA2005746EC /* TermsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TermsView.swift; sourceTree = ""; };
116 | 0EDCA2F41CEE365300A6E385 /* Router.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Router.swift; sourceTree = ""; };
117 | 7712177C1D7C89EA0070CDF9 /* BaseCoordinatorTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseCoordinatorTest.swift; sourceTree = ""; };
118 | 7712177E1D7C91670070CDF9 /* SettingsCoordinatorTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsCoordinatorTest.swift; sourceTree = ""; };
119 | 771217801D7C94290070CDF9 /* AuthCoordinatorTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = AuthCoordinatorTest.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
120 | 77ADE7671E9A724F00409A63 /* ApplicationCoordinator-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "ApplicationCoordinator-Bridging-Header.h"; path = "ApplicationCoordinator/Application/ApplicationCoordinator-Bridging-Header.h"; sourceTree = SOURCE_ROOT; };
121 | 77B3E7901D8E90550095527A /* ItemCoordinatorTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = ItemCoordinatorTest.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
122 | 77E054B01D79312700147DAE /* ApplicationCoordinatorTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ApplicationCoordinatorTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
123 | 77E054B41D79312700147DAE /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
124 | 77E054CA1D7937C900147DAE /* RouterTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RouterTest.swift; sourceTree = ""; };
125 | 77E054CC1D79391600147DAE /* RouterMock.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RouterMock.swift; sourceTree = ""; };
126 | AD1BFD581E2930C40022C865 /* DeepLinkOption.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeepLinkOption.swift; sourceTree = ""; };
127 | AD2BFC0A1E75B2730059C854 /* OnboardingModuleFactory.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OnboardingModuleFactory.swift; sourceTree = ""; };
128 | AD2BFC0D1E75B7FE0059C854 /* OnboardingController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OnboardingController.swift; sourceTree = ""; };
129 | AD2BFC0E1E75B7FE0059C854 /* PageViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PageViewController.swift; sourceTree = ""; };
130 | AD2BFC0F1E75B7FE0059C854 /* StepController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StepController.swift; sourceTree = ""; };
131 | AD2BFC131E75B81B0059C854 /* OnboardingView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OnboardingView.swift; sourceTree = ""; };
132 | AD2BFC151E75B95F0059C854 /* OnboardingCoordinatorOutput.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OnboardingCoordinatorOutput.swift; sourceTree = ""; };
133 | ADE320CD1E47941B0087146A /* OnboardingCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OnboardingCoordinator.swift; sourceTree = ""; };
134 | ADE320CF1E47943B0087146A /* Onboarding.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Onboarding.storyboard; sourceTree = ""; };
135 | ADE320F41E4796D90087146A /* TabbarController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TabbarController.swift; sourceTree = ""; };
136 | ADE320F51E4796D90087146A /* TabbarCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TabbarCoordinator.swift; sourceTree = ""; };
137 | ADE320F61E4796D90087146A /* TabbarView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TabbarView.swift; sourceTree = ""; };
138 | D50F9EEF1C7A525E00C5A59F /* ApplicationCoordinator.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ApplicationCoordinator.app; sourceTree = BUILT_PRODUCTS_DIR; };
139 | D5311AAD1CF8566D00777B87 /* ItemCreateController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; name = ItemCreateController.swift; path = "ApplicationCoordinator/Flows/Create flow/ItemCreateController.swift"; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
140 | D5311AAE1CF8566D00777B87 /* ItemCreateView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ItemCreateView.swift; path = "ApplicationCoordinator/Flows/Create flow/ItemCreateView.swift"; sourceTree = SOURCE_ROOT; };
141 | D538B0971D0722490073895D /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = ApplicationCoordinator/Resources/Info.plist; sourceTree = SOURCE_ROOT; };
142 | D53C00B91D1BBADA00573E71 /* Presentable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Presentable.swift; sourceTree = ""; };
143 | D541E7631CD9126F004C38B3 /* Coordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Coordinator.swift; sourceTree = ""; };
144 | D541E7651CD9128B004C38B3 /* BaseView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseView.swift; sourceTree = ""; };
145 | D541E7691CD91304004C38B3 /* RouterImp.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = RouterImp.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
146 | D545E4A11CD1FF40002B6F8F /* BaseCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseCoordinator.swift; sourceTree = ""; };
147 | D5505CC61CDF67F60012CBA6 /* UIViewExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIViewExtensions.swift; sourceTree = ""; };
148 | D5505CC81CDF68260012CBA6 /* NSObjectExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSObjectExtensions.swift; sourceTree = ""; };
149 | D5505CCA1CDF68D10012CBA6 /* StoryboardsEnum.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StoryboardsEnum.swift; sourceTree = ""; };
150 | D5FDC84D1CEB3579001C3DF3 /* LoginView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = LoginView.swift; path = ../LoginView.swift; sourceTree = ""; };
151 | D5FDC84F1CEB3AE0001C3DF3 /* SignUpView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SignUpView.swift; path = ../SignUpView.swift; sourceTree = ""; };
152 | D5FDC8531CEB56A8001C3DF3 /* SettingsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = ""; };
153 | D5FE33261D071ED900CE38B1 /* ModuleFactoryImp.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; name = ModuleFactoryImp.swift; path = ApplicationCoordinator/Factories/ModuleFactoryImp.swift; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
154 | D5FE332E1D071F4100CE38B1 /* CoordinatorFactoryImp.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; name = CoordinatorFactoryImp.swift; path = ApplicationCoordinator/Factories/CoordinatorFactoryImp.swift; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
155 | D5FE33301D071F6300CE38B1 /* ItemDetailController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ItemDetailController.swift; path = "ApplicationCoordinator/Flows/Items flow/ItemDetailController.swift"; sourceTree = SOURCE_ROOT; };
156 | D5FE33311D071F6300CE38B1 /* ItemDetailView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ItemDetailView.swift; path = "ApplicationCoordinator/Flows/Items flow/ItemDetailView.swift"; sourceTree = SOURCE_ROOT; };
157 | D5FE33321D071F6300CE38B1 /* ItemsListView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ItemsListView.swift; path = "ApplicationCoordinator/Flows/Items flow/ItemsListView.swift"; sourceTree = SOURCE_ROOT; };
158 | D5FE33331D071F6300CE38B1 /* ItemsListController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ItemsListController.swift; path = "ApplicationCoordinator/Flows/Items flow/ItemsListController.swift"; sourceTree = SOURCE_ROOT; };
159 | D5FE33421D0720E500CE38B1 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = ApplicationCoordinator/Images.xcassets; sourceTree = SOURCE_ROOT; };
160 | /* End PBXFileReference section */
161 |
162 | /* Begin PBXFrameworksBuildPhase section */
163 | 77E054AD1D79312700147DAE /* Frameworks */ = {
164 | isa = PBXFrameworksBuildPhase;
165 | buildActionMask = 2147483647;
166 | files = (
167 | );
168 | runOnlyForDeploymentPostprocessing = 0;
169 | };
170 | D50F9EEC1C7A525E00C5A59F /* Frameworks */ = {
171 | isa = PBXFrameworksBuildPhase;
172 | buildActionMask = 2147483647;
173 | files = (
174 | );
175 | runOnlyForDeploymentPostprocessing = 0;
176 | };
177 | /* End PBXFrameworksBuildPhase section */
178 |
179 | /* Begin PBXGroup section */
180 | 0E3CCF7C1CCBBB670022E830 /* Application */ = {
181 | isa = PBXGroup;
182 | children = (
183 | 0E3CCF7D1CCBBB670022E830 /* AppDelegate.swift */,
184 | 0E3CCF7E1CCBBB670022E830 /* ApplicationCoordinator.swift */,
185 | );
186 | name = Application;
187 | path = ApplicationCoordinator/Application;
188 | sourceTree = SOURCE_ROOT;
189 | };
190 | 0E3CCF811CCBBB670022E830 /* Base.lproj */ = {
191 | isa = PBXGroup;
192 | children = (
193 | 0E3CCF821CCBBB670022E830 /* LaunchScreen.storyboard */,
194 | );
195 | name = Base.lproj;
196 | path = ApplicationCoordinator/Base.lproj;
197 | sourceTree = SOURCE_ROOT;
198 | };
199 | 0E3CCF861CCBBB670022E830 /* Extensions */ = {
200 | isa = PBXGroup;
201 | children = (
202 | 0E3CCF881CCBBB670022E830 /* UIViewControllerExtension.swift */,
203 | D5505CC61CDF67F60012CBA6 /* UIViewExtensions.swift */,
204 | D5505CC81CDF68260012CBA6 /* NSObjectExtensions.swift */,
205 | );
206 | name = Extensions;
207 | path = ApplicationCoordinator/Extensions;
208 | sourceTree = SOURCE_ROOT;
209 | };
210 | 0E3CCF891CCBBB670022E830 /* Flows */ = {
211 | isa = PBXGroup;
212 | children = (
213 | ADE320F31E4796810087146A /* MainTabbarFlow */,
214 | ADE320CC1E4793F60087146A /* OnboardingFlow */,
215 | 0E3CCF8B1CCBBB670022E830 /* Items flow */,
216 | 0E3CCF8A1CCBBB670022E830 /* Create flow */,
217 | 0E3CCF8C1CCBBB670022E830 /* Login flow */,
218 | 0E3CCF8D1CCBBB670022E830 /* Settings flow */,
219 | );
220 | name = Flows;
221 | path = ApplicationCoordinator/Flows;
222 | sourceTree = SOURCE_ROOT;
223 | };
224 | 0E3CCF8A1CCBBB670022E830 /* Create flow */ = {
225 | isa = PBXGroup;
226 | children = (
227 | 0E6A42C91CF4E81E005746EC /* ItemCreateCoordinatorOutput.swift */,
228 | 0E3CCFC11CCBC0AA0022E830 /* ItemCreateCoordinator.swift */,
229 | 0E6A42CB1CF4E84D005746EC /* Controllers */,
230 | );
231 | path = "Create flow";
232 | sourceTree = "";
233 | };
234 | 0E3CCF8B1CCBBB670022E830 /* Items flow */ = {
235 | isa = PBXGroup;
236 | children = (
237 | 0E3CCFB31CCBBCA20022E830 /* ItemCoordinator.swift */,
238 | 0E6A42C81CF4E7F7005746EC /* Controllers */,
239 | );
240 | path = "Items flow";
241 | sourceTree = "";
242 | };
243 | 0E3CCF8C1CCBBB670022E830 /* Login flow */ = {
244 | isa = PBXGroup;
245 | children = (
246 | 0E6A42CC1CF4E862005746EC /* AuthCoordinatorOutput.swift */,
247 | 0E3CCFBB1CCBC0210022E830 /* AuthCoordinator.swift */,
248 | 0E6A42CE1CF4E88C005746EC /* Controllers */,
249 | );
250 | path = "Login flow";
251 | sourceTree = "";
252 | };
253 | 0E3CCF8D1CCBBB670022E830 /* Settings flow */ = {
254 | isa = PBXGroup;
255 | children = (
256 | 0E3CCFBD1CCBC04B0022E830 /* SettingsCoordinator.swift */,
257 | D5FDC8531CEB56A8001C3DF3 /* SettingsView.swift */,
258 | 0E3CCFBF1CCBC0600022E830 /* SettingsController.swift */,
259 | );
260 | path = "Settings flow";
261 | sourceTree = "";
262 | };
263 | 0E3CCF921CCBBB670022E830 /* Protocols */ = {
264 | isa = PBXGroup;
265 | children = (
266 | D541E7631CD9126F004C38B3 /* Coordinator.swift */,
267 | AD1BFD581E2930C40022C865 /* DeepLinkOption.swift */,
268 | D541E7651CD9128B004C38B3 /* BaseView.swift */,
269 | D53C00B91D1BBADA00573E71 /* Presentable.swift */,
270 | );
271 | name = Protocols;
272 | path = ApplicationCoordinator/Protocols;
273 | sourceTree = SOURCE_ROOT;
274 | };
275 | 0E3CCF961CCBBB670022E830 /* Storyboards */ = {
276 | isa = PBXGroup;
277 | children = (
278 | D5505CCA1CDF68D10012CBA6 /* StoryboardsEnum.swift */,
279 | 0E3CCF841CCBBB670022E830 /* Main.storyboard */,
280 | ADE320CF1E47943B0087146A /* Onboarding.storyboard */,
281 | 0E3CCF971CCBBB670022E830 /* Auth.storyboard */,
282 | 0E3CCF981CCBBB670022E830 /* Items.storyboard */,
283 | 0E3CCFAF1CCBBC760022E830 /* Create.storyboard */,
284 | 0E3CCFB01CCBBC760022E830 /* Settings.storyboard */,
285 | );
286 | name = Storyboards;
287 | path = ApplicationCoordinator/Storyboards;
288 | sourceTree = SOURCE_ROOT;
289 | };
290 | 0E3CCFAA1CCBBBFB0022E830 /* Resources */ = {
291 | isa = PBXGroup;
292 | children = (
293 | 77ADE7671E9A724F00409A63 /* ApplicationCoordinator-Bridging-Header.h */,
294 | D5FE33421D0720E500CE38B1 /* Images.xcassets */,
295 | D538B0971D0722490073895D /* Info.plist */,
296 | 0E3CCF811CCBBB670022E830 /* Base.lproj */,
297 | );
298 | name = Resources;
299 | sourceTree = "";
300 | };
301 | 0E3CCFC51CCBC6660022E830 /* Model */ = {
302 | isa = PBXGroup;
303 | children = (
304 | 0E3CCFC61CCBC6740022E830 /* ItemList.swift */,
305 | 0E3CCFCA1CCBFB150022E830 /* Setting.swift */,
306 | );
307 | name = Model;
308 | path = ApplicationCoordinator/Model;
309 | sourceTree = SOURCE_ROOT;
310 | };
311 | 0E6A42C71CF4E4C4005746EC /* Imp */ = {
312 | isa = PBXGroup;
313 | children = (
314 | D5FE332E1D071F4100CE38B1 /* CoordinatorFactoryImp.swift */,
315 | D5FE33261D071ED900CE38B1 /* ModuleFactoryImp.swift */,
316 | );
317 | name = Imp;
318 | sourceTree = "";
319 | };
320 | 0E6A42C81CF4E7F7005746EC /* Controllers */ = {
321 | isa = PBXGroup;
322 | children = (
323 | D5FE33321D071F6300CE38B1 /* ItemsListView.swift */,
324 | D5FE33331D071F6300CE38B1 /* ItemsListController.swift */,
325 | D5FE33311D071F6300CE38B1 /* ItemDetailView.swift */,
326 | D5FE33301D071F6300CE38B1 /* ItemDetailController.swift */,
327 | );
328 | name = Controllers;
329 | sourceTree = "";
330 | };
331 | 0E6A42CB1CF4E84D005746EC /* Controllers */ = {
332 | isa = PBXGroup;
333 | children = (
334 | D5311AAE1CF8566D00777B87 /* ItemCreateView.swift */,
335 | D5311AAD1CF8566D00777B87 /* ItemCreateController.swift */,
336 | );
337 | path = Controllers;
338 | sourceTree = "";
339 | };
340 | 0E6A42CE1CF4E88C005746EC /* Controllers */ = {
341 | isa = PBXGroup;
342 | children = (
343 | D5FDC84D1CEB3579001C3DF3 /* LoginView.swift */,
344 | 0E3CCFB91CCBBFEF0022E830 /* LoginController.swift */,
345 | D5FDC84F1CEB3AE0001C3DF3 /* SignUpView.swift */,
346 | 0E3CCFC81CCBE87F0022E830 /* SignUpController.swift */,
347 | 0E6A42D31CF62EA2005746EC /* TermsView.swift */,
348 | 0E6A42D11CF627CC005746EC /* TermsController.swift */,
349 | );
350 | path = Controllers;
351 | sourceTree = "";
352 | };
353 | 77E054B11D79312700147DAE /* ApplicationCoordinatorTests */ = {
354 | isa = PBXGroup;
355 | children = (
356 | 77E054C91D79378400147DAE /* Factory */,
357 | 77E054C61D79377300147DAE /* Application */,
358 | 77E054C71D79377300147DAE /* Parent */,
359 | 77E054C81D79377300147DAE /* Router */,
360 | 77E054C21D79370D00147DAE /* CreateFlow */,
361 | 77E054C31D79370D00147DAE /* ItemsFlow */,
362 | 77E054C41D79370D00147DAE /* LoginFlow */,
363 | 77E054C51D79370D00147DAE /* SettingsFlow */,
364 | 77E054B41D79312700147DAE /* Info.plist */,
365 | );
366 | path = ApplicationCoordinatorTests;
367 | sourceTree = "";
368 | };
369 | 77E054C21D79370D00147DAE /* CreateFlow */ = {
370 | isa = PBXGroup;
371 | children = (
372 | );
373 | path = CreateFlow;
374 | sourceTree = "";
375 | };
376 | 77E054C31D79370D00147DAE /* ItemsFlow */ = {
377 | isa = PBXGroup;
378 | children = (
379 | 77B3E7901D8E90550095527A /* ItemCoordinatorTest.swift */,
380 | );
381 | path = ItemsFlow;
382 | sourceTree = "";
383 | };
384 | 77E054C41D79370D00147DAE /* LoginFlow */ = {
385 | isa = PBXGroup;
386 | children = (
387 | 771217801D7C94290070CDF9 /* AuthCoordinatorTest.swift */,
388 | );
389 | path = LoginFlow;
390 | sourceTree = "";
391 | };
392 | 77E054C51D79370D00147DAE /* SettingsFlow */ = {
393 | isa = PBXGroup;
394 | children = (
395 | 7712177E1D7C91670070CDF9 /* SettingsCoordinatorTest.swift */,
396 | );
397 | path = SettingsFlow;
398 | sourceTree = "";
399 | };
400 | 77E054C61D79377300147DAE /* Application */ = {
401 | isa = PBXGroup;
402 | children = (
403 | );
404 | path = Application;
405 | sourceTree = "";
406 | };
407 | 77E054C71D79377300147DAE /* Parent */ = {
408 | isa = PBXGroup;
409 | children = (
410 | 7712177C1D7C89EA0070CDF9 /* BaseCoordinatorTest.swift */,
411 | );
412 | path = Parent;
413 | sourceTree = "";
414 | };
415 | 77E054C81D79377300147DAE /* Router */ = {
416 | isa = PBXGroup;
417 | children = (
418 | 77E054CA1D7937C900147DAE /* RouterTest.swift */,
419 | 77E054CC1D79391600147DAE /* RouterMock.swift */,
420 | );
421 | path = Router;
422 | sourceTree = "";
423 | };
424 | 77E054C91D79378400147DAE /* Factory */ = {
425 | isa = PBXGroup;
426 | children = (
427 | );
428 | path = Factory;
429 | sourceTree = "";
430 | };
431 | AD1BFD531E2926040022C865 /* Frameworks */ = {
432 | isa = PBXGroup;
433 | children = (
434 | );
435 | name = Frameworks;
436 | sourceTree = "";
437 | };
438 | AD2BFC0C1E75B7FE0059C854 /* Controllers */ = {
439 | isa = PBXGroup;
440 | children = (
441 | AD2BFC131E75B81B0059C854 /* OnboardingView.swift */,
442 | AD2BFC0D1E75B7FE0059C854 /* OnboardingController.swift */,
443 | AD2BFC0E1E75B7FE0059C854 /* PageViewController.swift */,
444 | AD2BFC0F1E75B7FE0059C854 /* StepController.swift */,
445 | );
446 | path = Controllers;
447 | sourceTree = "";
448 | };
449 | ADE320CC1E4793F60087146A /* OnboardingFlow */ = {
450 | isa = PBXGroup;
451 | children = (
452 | AD2BFC151E75B95F0059C854 /* OnboardingCoordinatorOutput.swift */,
453 | ADE320CD1E47941B0087146A /* OnboardingCoordinator.swift */,
454 | AD2BFC0C1E75B7FE0059C854 /* Controllers */,
455 | );
456 | path = OnboardingFlow;
457 | sourceTree = "";
458 | };
459 | ADE320F31E4796810087146A /* MainTabbarFlow */ = {
460 | isa = PBXGroup;
461 | children = (
462 | ADE320F51E4796D90087146A /* TabbarCoordinator.swift */,
463 | ADE320F61E4796D90087146A /* TabbarView.swift */,
464 | ADE320F41E4796D90087146A /* TabbarController.swift */,
465 | );
466 | path = MainTabbarFlow;
467 | sourceTree = "";
468 | };
469 | D50F9EE61C7A525E00C5A59F = {
470 | isa = PBXGroup;
471 | children = (
472 | D50F9EF11C7A525E00C5A59F /* ApplicationCoordinator */,
473 | 77E054B11D79312700147DAE /* ApplicationCoordinatorTests */,
474 | D50F9EF01C7A525E00C5A59F /* Products */,
475 | AD1BFD531E2926040022C865 /* Frameworks */,
476 | );
477 | sourceTree = "";
478 | };
479 | D50F9EF01C7A525E00C5A59F /* Products */ = {
480 | isa = PBXGroup;
481 | children = (
482 | D50F9EEF1C7A525E00C5A59F /* ApplicationCoordinator.app */,
483 | 77E054B01D79312700147DAE /* ApplicationCoordinatorTests.xctest */,
484 | );
485 | name = Products;
486 | sourceTree = "";
487 | };
488 | D50F9EF11C7A525E00C5A59F /* ApplicationCoordinator */ = {
489 | isa = PBXGroup;
490 | children = (
491 | 0E3CCF7C1CCBBB670022E830 /* Application */,
492 | 0E3CCF921CCBBB670022E830 /* Protocols */,
493 | D541E7681CD912F3004C38B3 /* Router */,
494 | 0E3CCF861CCBBB670022E830 /* Extensions */,
495 | 0E3CCFC51CCBC6660022E830 /* Model */,
496 | D541E7671CD912BD004C38B3 /* Factories */,
497 | D545E4A01CD1FEFB002B6F8F /* Parents */,
498 | 0E3CCF891CCBBB670022E830 /* Flows */,
499 | 0E3CCF961CCBBB670022E830 /* Storyboards */,
500 | 0E3CCFAA1CCBBBFB0022E830 /* Resources */,
501 | );
502 | name = ApplicationCoordinator;
503 | sourceTree = "";
504 | };
505 | D541E7671CD912BD004C38B3 /* Factories */ = {
506 | isa = PBXGroup;
507 | children = (
508 | 0E6A42C71CF4E4C4005746EC /* Imp */,
509 | 0E6A42BD1CF4DE62005746EC /* CoordinatorFactory.swift */,
510 | 0E6A42BF1CF4E06C005746EC /* ItemModuleFactory.swift */,
511 | 0E6A42C11CF4E29F005746EC /* ItemCreateModuleFactory.swift */,
512 | 0E6A42C31CF4E300005746EC /* AuthModuleFactory.swift */,
513 | 0E6A42C51CF4E45F005746EC /* SettingsModuleFactory.swift */,
514 | AD2BFC0A1E75B2730059C854 /* OnboardingModuleFactory.swift */,
515 | );
516 | name = Factories;
517 | path = ApplicationCoordinator/Factories;
518 | sourceTree = SOURCE_ROOT;
519 | };
520 | D541E7681CD912F3004C38B3 /* Router */ = {
521 | isa = PBXGroup;
522 | children = (
523 | 0EDCA2F41CEE365300A6E385 /* Router.swift */,
524 | D541E7691CD91304004C38B3 /* RouterImp.swift */,
525 | );
526 | name = Router;
527 | path = ApplicationCoordinator/Presenters;
528 | sourceTree = SOURCE_ROOT;
529 | };
530 | D545E4A01CD1FEFB002B6F8F /* Parents */ = {
531 | isa = PBXGroup;
532 | children = (
533 | D545E4A11CD1FF40002B6F8F /* BaseCoordinator.swift */,
534 | );
535 | name = Parents;
536 | path = ApplicationCoordinator/Parents;
537 | sourceTree = SOURCE_ROOT;
538 | };
539 | /* End PBXGroup section */
540 |
541 | /* Begin PBXNativeTarget section */
542 | 77E054AF1D79312700147DAE /* ApplicationCoordinatorTests */ = {
543 | isa = PBXNativeTarget;
544 | buildConfigurationList = 77E054B91D79312700147DAE /* Build configuration list for PBXNativeTarget "ApplicationCoordinatorTests" */;
545 | buildPhases = (
546 | 77E054AC1D79312700147DAE /* Sources */,
547 | 77E054AD1D79312700147DAE /* Frameworks */,
548 | 77E054AE1D79312700147DAE /* Resources */,
549 | );
550 | buildRules = (
551 | );
552 | dependencies = (
553 | 77E054B61D79312700147DAE /* PBXTargetDependency */,
554 | );
555 | name = ApplicationCoordinatorTests;
556 | productName = ApplicationCoordinatorTests;
557 | productReference = 77E054B01D79312700147DAE /* ApplicationCoordinatorTests.xctest */;
558 | productType = "com.apple.product-type.bundle.unit-test";
559 | };
560 | D50F9EEE1C7A525E00C5A59F /* ApplicationCoordinator */ = {
561 | isa = PBXNativeTarget;
562 | buildConfigurationList = D50F9F011C7A525E00C5A59F /* Build configuration list for PBXNativeTarget "ApplicationCoordinator" */;
563 | buildPhases = (
564 | D50F9EEB1C7A525E00C5A59F /* Sources */,
565 | D50F9EEC1C7A525E00C5A59F /* Frameworks */,
566 | D50F9EED1C7A525E00C5A59F /* Resources */,
567 | );
568 | buildRules = (
569 | );
570 | dependencies = (
571 | );
572 | name = ApplicationCoordinator;
573 | productName = ApplicationCoordinatorExample;
574 | productReference = D50F9EEF1C7A525E00C5A59F /* ApplicationCoordinator.app */;
575 | productType = "com.apple.product-type.application";
576 | };
577 | /* End PBXNativeTarget section */
578 |
579 | /* Begin PBXProject section */
580 | D50F9EE71C7A525E00C5A59F /* Project object */ = {
581 | isa = PBXProject;
582 | attributes = {
583 | LastSwiftUpdateCheck = 0730;
584 | LastUpgradeCheck = 1400;
585 | ORGANIZATIONNAME = "Andrey Panov";
586 | TargetAttributes = {
587 | 77E054AF1D79312700147DAE = {
588 | CreatedOnToolsVersion = 7.3.1;
589 | LastSwiftMigration = 1100;
590 | TestTargetID = D50F9EEE1C7A525E00C5A59F;
591 | };
592 | D50F9EEE1C7A525E00C5A59F = {
593 | CreatedOnToolsVersion = 7.2.1;
594 | DevelopmentTeam = CMWP53JFC4;
595 | LastSwiftMigration = 1100;
596 | };
597 | };
598 | };
599 | buildConfigurationList = D50F9EEA1C7A525E00C5A59F /* Build configuration list for PBXProject "ApplicationCoordinator" */;
600 | compatibilityVersion = "Xcode 3.2";
601 | developmentRegion = en;
602 | hasScannedForEncodings = 0;
603 | knownRegions = (
604 | en,
605 | Base,
606 | );
607 | mainGroup = D50F9EE61C7A525E00C5A59F;
608 | productRefGroup = D50F9EF01C7A525E00C5A59F /* Products */;
609 | projectDirPath = "";
610 | projectRoot = "";
611 | targets = (
612 | D50F9EEE1C7A525E00C5A59F /* ApplicationCoordinator */,
613 | 77E054AF1D79312700147DAE /* ApplicationCoordinatorTests */,
614 | );
615 | };
616 | /* End PBXProject section */
617 |
618 | /* Begin PBXResourcesBuildPhase section */
619 | 77E054AE1D79312700147DAE /* Resources */ = {
620 | isa = PBXResourcesBuildPhase;
621 | buildActionMask = 2147483647;
622 | files = (
623 | );
624 | runOnlyForDeploymentPostprocessing = 0;
625 | };
626 | D50F9EED1C7A525E00C5A59F /* Resources */ = {
627 | isa = PBXResourcesBuildPhase;
628 | buildActionMask = 2147483647;
629 | files = (
630 | 0E3CCFB11CCBBC760022E830 /* Create.storyboard in Resources */,
631 | 0E3CCFA91CCBBB670022E830 /* Items.storyboard in Resources */,
632 | D5FE33431D0720E500CE38B1 /* Images.xcassets in Resources */,
633 | 0E3CCF9E1CCBBB670022E830 /* Main.storyboard in Resources */,
634 | 0E3CCF9D1CCBBB670022E830 /* LaunchScreen.storyboard in Resources */,
635 | 0E3CCFA81CCBBB670022E830 /* Auth.storyboard in Resources */,
636 | 0E3CCFB21CCBBC760022E830 /* Settings.storyboard in Resources */,
637 | ADE320D01E47943B0087146A /* Onboarding.storyboard in Resources */,
638 | );
639 | runOnlyForDeploymentPostprocessing = 0;
640 | };
641 | /* End PBXResourcesBuildPhase section */
642 |
643 | /* Begin PBXSourcesBuildPhase section */
644 | 77E054AC1D79312700147DAE /* Sources */ = {
645 | isa = PBXSourcesBuildPhase;
646 | buildActionMask = 2147483647;
647 | files = (
648 | 77E054CD1D79391600147DAE /* RouterMock.swift in Sources */,
649 | 77B3E7911D8E90550095527A /* ItemCoordinatorTest.swift in Sources */,
650 | 7712177F1D7C91670070CDF9 /* SettingsCoordinatorTest.swift in Sources */,
651 | 77E054CB1D7937C900147DAE /* RouterTest.swift in Sources */,
652 | 7712177D1D7C89EA0070CDF9 /* BaseCoordinatorTest.swift in Sources */,
653 | 771217811D7C94290070CDF9 /* AuthCoordinatorTest.swift in Sources */,
654 | );
655 | runOnlyForDeploymentPostprocessing = 0;
656 | };
657 | D50F9EEB1C7A525E00C5A59F /* Sources */ = {
658 | isa = PBXSourcesBuildPhase;
659 | buildActionMask = 2147483647;
660 | files = (
661 | 0E3CCF991CCBBB670022E830 /* AppDelegate.swift in Sources */,
662 | 0E6A42C01CF4E06C005746EC /* ItemModuleFactory.swift in Sources */,
663 | 0E3CCFBA1CCBBFEF0022E830 /* LoginController.swift in Sources */,
664 | 0E3CCFC71CCBC6740022E830 /* ItemList.swift in Sources */,
665 | ADE320F81E4796D90087146A /* TabbarCoordinator.swift in Sources */,
666 | 0E6A42C41CF4E300005746EC /* AuthModuleFactory.swift in Sources */,
667 | D541E7661CD9128B004C38B3 /* BaseView.swift in Sources */,
668 | 0E6A42C21CF4E29F005746EC /* ItemCreateModuleFactory.swift in Sources */,
669 | D53C00BA1D1BBADA00573E71 /* Presentable.swift in Sources */,
670 | 0E6A42D21CF627CC005746EC /* TermsController.swift in Sources */,
671 | 0EDCA2F51CEE365300A6E385 /* Router.swift in Sources */,
672 | 0E3CCFB41CCBBCA20022E830 /* ItemCoordinator.swift in Sources */,
673 | D5505CCB1CDF68D10012CBA6 /* StoryboardsEnum.swift in Sources */,
674 | AD2BFC161E75B95F0059C854 /* OnboardingCoordinatorOutput.swift in Sources */,
675 | D5311AAF1CF8566D00777B87 /* ItemCreateController.swift in Sources */,
676 | 0E3CCFBC1CCBC0210022E830 /* AuthCoordinator.swift in Sources */,
677 | AD2BFC121E75B7FE0059C854 /* StepController.swift in Sources */,
678 | D5FE33371D071F6300CE38B1 /* ItemsListController.swift in Sources */,
679 | 0E3CCFC01CCBC0600022E830 /* SettingsController.swift in Sources */,
680 | 0E6A42BE1CF4DE62005746EC /* CoordinatorFactory.swift in Sources */,
681 | D5505CC91CDF68260012CBA6 /* NSObjectExtensions.swift in Sources */,
682 | ADE320CE1E47941B0087146A /* OnboardingCoordinator.swift in Sources */,
683 | AD2BFC141E75B81B0059C854 /* OnboardingView.swift in Sources */,
684 | 0E6A42CA1CF4E81E005746EC /* ItemCreateCoordinatorOutput.swift in Sources */,
685 | D541E76A1CD91304004C38B3 /* RouterImp.swift in Sources */,
686 | D5FDC8501CEB3AE0001C3DF3 /* SignUpView.swift in Sources */,
687 | AD2BFC111E75B7FE0059C854 /* PageViewController.swift in Sources */,
688 | 0E6A42D41CF62EA2005746EC /* TermsView.swift in Sources */,
689 | D5FDC84E1CEB3579001C3DF3 /* LoginView.swift in Sources */,
690 | 0E3CCFC91CCBE87F0022E830 /* SignUpController.swift in Sources */,
691 | D5FE33361D071F6300CE38B1 /* ItemsListView.swift in Sources */,
692 | D5311AB01CF8566D00777B87 /* ItemCreateView.swift in Sources */,
693 | AD2BFC0B1E75B2730059C854 /* OnboardingModuleFactory.swift in Sources */,
694 | AD2BFC101E75B7FE0059C854 /* OnboardingController.swift in Sources */,
695 | D5FE332A1D071ED900CE38B1 /* ModuleFactoryImp.swift in Sources */,
696 | D5505CC71CDF67F60012CBA6 /* UIViewExtensions.swift in Sources */,
697 | AD1BFD591E2930C40022C865 /* DeepLinkOption.swift in Sources */,
698 | D5FE332F1D071F4100CE38B1 /* CoordinatorFactoryImp.swift in Sources */,
699 | 0E6A42C61CF4E45F005746EC /* SettingsModuleFactory.swift in Sources */,
700 | ADE320F91E4796D90087146A /* TabbarView.swift in Sources */,
701 | 0E3CCFA01CCBBB670022E830 /* UIViewControllerExtension.swift in Sources */,
702 | 0E3CCFC21CCBC0AA0022E830 /* ItemCreateCoordinator.swift in Sources */,
703 | D5FE33351D071F6300CE38B1 /* ItemDetailView.swift in Sources */,
704 | 0E3CCF9A1CCBBB670022E830 /* ApplicationCoordinator.swift in Sources */,
705 | D5FE33341D071F6300CE38B1 /* ItemDetailController.swift in Sources */,
706 | D5FDC8541CEB56A8001C3DF3 /* SettingsView.swift in Sources */,
707 | 0E6A42CD1CF4E862005746EC /* AuthCoordinatorOutput.swift in Sources */,
708 | D545E4A21CD1FF40002B6F8F /* BaseCoordinator.swift in Sources */,
709 | 0E3CCFBE1CCBC04B0022E830 /* SettingsCoordinator.swift in Sources */,
710 | 0E3CCFCB1CCBFB150022E830 /* Setting.swift in Sources */,
711 | D541E7641CD9126F004C38B3 /* Coordinator.swift in Sources */,
712 | ADE320F71E4796D90087146A /* TabbarController.swift in Sources */,
713 | );
714 | runOnlyForDeploymentPostprocessing = 0;
715 | };
716 | /* End PBXSourcesBuildPhase section */
717 |
718 | /* Begin PBXTargetDependency section */
719 | 77E054B61D79312700147DAE /* PBXTargetDependency */ = {
720 | isa = PBXTargetDependency;
721 | target = D50F9EEE1C7A525E00C5A59F /* ApplicationCoordinator */;
722 | targetProxy = 77E054B51D79312700147DAE /* PBXContainerItemProxy */;
723 | };
724 | /* End PBXTargetDependency section */
725 |
726 | /* Begin PBXVariantGroup section */
727 | 0E3CCF821CCBBB670022E830 /* LaunchScreen.storyboard */ = {
728 | isa = PBXVariantGroup;
729 | children = (
730 | 0E3CCF831CCBBB670022E830 /* Base */,
731 | );
732 | name = LaunchScreen.storyboard;
733 | sourceTree = "";
734 | };
735 | 0E3CCF841CCBBB670022E830 /* Main.storyboard */ = {
736 | isa = PBXVariantGroup;
737 | children = (
738 | 0E3CCF851CCBBB670022E830 /* Base */,
739 | );
740 | name = Main.storyboard;
741 | path = ../Base.lproj;
742 | sourceTree = "";
743 | };
744 | /* End PBXVariantGroup section */
745 |
746 | /* Begin XCBuildConfiguration section */
747 | 77E054B71D79312700147DAE /* Debug */ = {
748 | isa = XCBuildConfiguration;
749 | buildSettings = {
750 | BUNDLE_LOADER = "$(TEST_HOST)";
751 | CLANG_ANALYZER_NONNULL = YES;
752 | CLANG_ENABLE_MODULES = YES;
753 | INFOPLIST_FILE = ApplicationCoordinatorTests/Info.plist;
754 | IPHONEOS_DEPLOYMENT_TARGET = 12.0;
755 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
756 | PRODUCT_BUNDLE_IDENTIFIER = com.panovdev.ApplicationCoordinatorTests;
757 | PRODUCT_NAME = "$(TARGET_NAME)";
758 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
759 | SWIFT_SWIFT3_OBJC_INFERENCE = Off;
760 | SWIFT_VERSION = 5.0;
761 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ApplicationCoordinator.app/ApplicationCoordinator";
762 | };
763 | name = Debug;
764 | };
765 | 77E054B81D79312700147DAE /* Release */ = {
766 | isa = XCBuildConfiguration;
767 | buildSettings = {
768 | BUNDLE_LOADER = "$(TEST_HOST)";
769 | CLANG_ANALYZER_NONNULL = YES;
770 | CLANG_ENABLE_MODULES = YES;
771 | INFOPLIST_FILE = ApplicationCoordinatorTests/Info.plist;
772 | IPHONEOS_DEPLOYMENT_TARGET = 12.0;
773 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
774 | PRODUCT_BUNDLE_IDENTIFIER = com.panovdev.ApplicationCoordinatorTests;
775 | PRODUCT_NAME = "$(TARGET_NAME)";
776 | SWIFT_SWIFT3_OBJC_INFERENCE = Off;
777 | SWIFT_VERSION = 5.0;
778 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ApplicationCoordinator.app/ApplicationCoordinator";
779 | };
780 | name = Release;
781 | };
782 | D50F9EFF1C7A525E00C5A59F /* Debug */ = {
783 | isa = XCBuildConfiguration;
784 | buildSettings = {
785 | ALWAYS_SEARCH_USER_PATHS = NO;
786 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
787 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
788 | CLANG_CXX_LIBRARY = "libc++";
789 | CLANG_ENABLE_MODULES = YES;
790 | CLANG_ENABLE_OBJC_ARC = YES;
791 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
792 | CLANG_WARN_BOOL_CONVERSION = YES;
793 | CLANG_WARN_COMMA = YES;
794 | CLANG_WARN_CONSTANT_CONVERSION = YES;
795 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
796 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
797 | CLANG_WARN_EMPTY_BODY = YES;
798 | CLANG_WARN_ENUM_CONVERSION = YES;
799 | CLANG_WARN_INFINITE_RECURSION = YES;
800 | CLANG_WARN_INT_CONVERSION = YES;
801 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
802 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
803 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
804 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
805 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
806 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
807 | CLANG_WARN_STRICT_PROTOTYPES = YES;
808 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
809 | CLANG_WARN_UNREACHABLE_CODE = YES;
810 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
811 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
812 | COPY_PHASE_STRIP = NO;
813 | DEBUG_INFORMATION_FORMAT = dwarf;
814 | ENABLE_STRICT_OBJC_MSGSEND = YES;
815 | ENABLE_TESTABILITY = YES;
816 | GCC_C_LANGUAGE_STANDARD = gnu99;
817 | GCC_DYNAMIC_NO_PIC = NO;
818 | GCC_NO_COMMON_BLOCKS = YES;
819 | GCC_OPTIMIZATION_LEVEL = 0;
820 | GCC_PREPROCESSOR_DEFINITIONS = (
821 | "DEBUG=1",
822 | "$(inherited)",
823 | );
824 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
825 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
826 | GCC_WARN_UNDECLARED_SELECTOR = YES;
827 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
828 | GCC_WARN_UNUSED_FUNCTION = YES;
829 | GCC_WARN_UNUSED_VARIABLE = YES;
830 | IPHONEOS_DEPLOYMENT_TARGET = 12.0;
831 | MTL_ENABLE_DEBUG_INFO = YES;
832 | ONLY_ACTIVE_ARCH = YES;
833 | SDKROOT = iphoneos;
834 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
835 | };
836 | name = Debug;
837 | };
838 | D50F9F001C7A525E00C5A59F /* Release */ = {
839 | isa = XCBuildConfiguration;
840 | buildSettings = {
841 | ALWAYS_SEARCH_USER_PATHS = NO;
842 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
843 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
844 | CLANG_CXX_LIBRARY = "libc++";
845 | CLANG_ENABLE_MODULES = YES;
846 | CLANG_ENABLE_OBJC_ARC = YES;
847 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
848 | CLANG_WARN_BOOL_CONVERSION = YES;
849 | CLANG_WARN_COMMA = YES;
850 | CLANG_WARN_CONSTANT_CONVERSION = YES;
851 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
852 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
853 | CLANG_WARN_EMPTY_BODY = YES;
854 | CLANG_WARN_ENUM_CONVERSION = YES;
855 | CLANG_WARN_INFINITE_RECURSION = YES;
856 | CLANG_WARN_INT_CONVERSION = YES;
857 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
858 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
859 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
860 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
861 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
862 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
863 | CLANG_WARN_STRICT_PROTOTYPES = YES;
864 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
865 | CLANG_WARN_UNREACHABLE_CODE = YES;
866 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
867 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
868 | COPY_PHASE_STRIP = NO;
869 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
870 | ENABLE_NS_ASSERTIONS = NO;
871 | ENABLE_STRICT_OBJC_MSGSEND = YES;
872 | GCC_C_LANGUAGE_STANDARD = gnu99;
873 | GCC_NO_COMMON_BLOCKS = YES;
874 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
875 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
876 | GCC_WARN_UNDECLARED_SELECTOR = YES;
877 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
878 | GCC_WARN_UNUSED_FUNCTION = YES;
879 | GCC_WARN_UNUSED_VARIABLE = YES;
880 | IPHONEOS_DEPLOYMENT_TARGET = 12.0;
881 | MTL_ENABLE_DEBUG_INFO = NO;
882 | SDKROOT = iphoneos;
883 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
884 | VALIDATE_PRODUCT = YES;
885 | };
886 | name = Release;
887 | };
888 | D50F9F021C7A525E00C5A59F /* Debug */ = {
889 | isa = XCBuildConfiguration;
890 | buildSettings = {
891 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
892 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = "Brand Assets";
893 | CLANG_ENABLE_MODULES = YES;
894 | DEVELOPMENT_TEAM = CMWP53JFC4;
895 | INFOPLIST_FILE = "$(SRCROOT)/ApplicationCoordinator/Resources/Info.plist";
896 | IPHONEOS_DEPLOYMENT_TARGET = 15.0;
897 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
898 | PRODUCT_BUNDLE_IDENTIFIER = com.panovdev.ApplicationCoordinator;
899 | PRODUCT_NAME = ApplicationCoordinator;
900 | SWIFT_OBJC_BRIDGING_HEADER = "ApplicationCoordinator/Application/ApplicationCoordinator-Bridging-Header.h";
901 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
902 | SWIFT_SWIFT3_OBJC_INFERENCE = Off;
903 | SWIFT_VERSION = 5.0;
904 | };
905 | name = Debug;
906 | };
907 | D50F9F031C7A525E00C5A59F /* Release */ = {
908 | isa = XCBuildConfiguration;
909 | buildSettings = {
910 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
911 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = "Brand Assets";
912 | CLANG_ENABLE_MODULES = YES;
913 | DEVELOPMENT_TEAM = CMWP53JFC4;
914 | INFOPLIST_FILE = "$(SRCROOT)/ApplicationCoordinator/Resources/Info.plist";
915 | IPHONEOS_DEPLOYMENT_TARGET = 15.0;
916 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
917 | PRODUCT_BUNDLE_IDENTIFIER = com.panovdev.ApplicationCoordinator;
918 | PRODUCT_NAME = ApplicationCoordinator;
919 | SWIFT_OBJC_BRIDGING_HEADER = "ApplicationCoordinator/Application/ApplicationCoordinator-Bridging-Header.h";
920 | SWIFT_SWIFT3_OBJC_INFERENCE = Off;
921 | SWIFT_VERSION = 5.0;
922 | };
923 | name = Release;
924 | };
925 | /* End XCBuildConfiguration section */
926 |
927 | /* Begin XCConfigurationList section */
928 | 77E054B91D79312700147DAE /* Build configuration list for PBXNativeTarget "ApplicationCoordinatorTests" */ = {
929 | isa = XCConfigurationList;
930 | buildConfigurations = (
931 | 77E054B71D79312700147DAE /* Debug */,
932 | 77E054B81D79312700147DAE /* Release */,
933 | );
934 | defaultConfigurationIsVisible = 0;
935 | defaultConfigurationName = Release;
936 | };
937 | D50F9EEA1C7A525E00C5A59F /* Build configuration list for PBXProject "ApplicationCoordinator" */ = {
938 | isa = XCConfigurationList;
939 | buildConfigurations = (
940 | D50F9EFF1C7A525E00C5A59F /* Debug */,
941 | D50F9F001C7A525E00C5A59F /* Release */,
942 | );
943 | defaultConfigurationIsVisible = 0;
944 | defaultConfigurationName = Release;
945 | };
946 | D50F9F011C7A525E00C5A59F /* Build configuration list for PBXNativeTarget "ApplicationCoordinator" */ = {
947 | isa = XCConfigurationList;
948 | buildConfigurations = (
949 | D50F9F021C7A525E00C5A59F /* Debug */,
950 | D50F9F031C7A525E00C5A59F /* Release */,
951 | );
952 | defaultConfigurationIsVisible = 0;
953 | defaultConfigurationName = Release;
954 | };
955 | /* End XCConfigurationList section */
956 | };
957 | rootObject = D50F9EE71C7A525E00C5A59F /* Project object */;
958 | }
959 |
--------------------------------------------------------------------------------