Bird
Raised Fist0
iOS Swiftmobile~20 mins

Coordinator pattern in iOS Swift - Mini App: Build & Ship

Choose your learning style10 modes available
Build: MainCoordinator Example
Build a simple app using the Coordinator pattern to manage navigation between two screens: Home and Details. The Home screen has a button to go to Details. The Coordinator handles the navigation logic.
Target UI
┌─────────────────────────┐
│        Home Screen       │
│                         │
│   [Go to Details Button] │
│                         │
└─────────────────────────┘

After tapping button:

┌─────────────────────────┐
│       Details Screen     │
│                         │
│   [Back Button (NavBar)] │
│                         │
└─────────────────────────┘
Create a Coordinator protocol with start() method
Implement MainCoordinator class to manage UINavigationController
HomeViewController with a button labeled 'Go to Details'
DetailsViewController with a simple label 'Details Screen'
Navigation from Home to Details handled by MainCoordinator
Use UINavigationController for navigation stack
Back button in Details screen to return to Home
Starter Code
iOS Swift
import UIKit

protocol Coordinator {
    func start()
}

class MainCoordinator: Coordinator {
    let navigationController: UINavigationController

    init(navigationController: UINavigationController) {
        self.navigationController = navigationController
    }

    func start() {
        // TODO: Show HomeViewController
    }

    func showDetails() {
        // TODO: Show DetailsViewController
    }
}

class HomeViewController: UIViewController {
    var coordinator: MainCoordinator?

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white
        // TODO: Add button to go to Details
    }

    @objc func goToDetailsTapped() {
        // TODO: Notify coordinator to show details
    }
}

class DetailsViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white
        // TODO: Add label 'Details Screen'
    }
}

@main
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?
    var coordinator: MainCoordinator?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        let navController = UINavigationController()
        coordinator = MainCoordinator(navigationController: navController)
        coordinator?.start()

        window = UIWindow(frame: UIScreen.main.bounds)
        window?.rootViewController = navController
        window?.makeKeyAndVisible()

        return true
    }
}
Task 1
Task 2
Task 3
Task 4
Task 5
Solution
iOS Swift
import UIKit

protocol Coordinator {
    func start()
}

class MainCoordinator: Coordinator {
    let navigationController: UINavigationController

    init(navigationController: UINavigationController) {
        self.navigationController = navigationController
    }

    func start() {
        let homeVC = HomeViewController()
        homeVC.coordinator = self
        navigationController.pushViewController(homeVC, animated: false)
    }

    func showDetails() {
        let detailsVC = DetailsViewController()
        navigationController.pushViewController(detailsVC, animated: true)
    }
}

class HomeViewController: UIViewController {
    var coordinator: MainCoordinator?

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white
        title = "Home"

        let button = UIButton(type: .system)
        button.setTitle("Go to Details", for: .normal)
        button.translatesAutoresizingMaskIntoConstraints = false
        button.addTarget(self, action: #selector(goToDetailsTapped), for: .touchUpInside)

        view.addSubview(button)

        NSLayoutConstraint.activate([
            button.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            button.centerYAnchor.constraint(equalTo: view.centerYAnchor)
        ])
    }

    @objc func goToDetailsTapped() {
        coordinator?.showDetails()
    }
}

class DetailsViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white
        title = "Details"

        let label = UILabel()
        label.text = "Details Screen"
        label.font = UIFont.systemFont(ofSize: 24, weight: .medium)
        label.translatesAutoresizingMaskIntoConstraints = false

        view.addSubview(label)

        NSLayoutConstraint.activate([
            label.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            label.centerYAnchor.constraint(equalTo: view.centerYAnchor)
        ])
    }
}

@main
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?
    var coordinator: MainCoordinator?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        let navController = UINavigationController()
        coordinator = MainCoordinator(navigationController: navController)
        coordinator?.start()

        window = UIWindow(frame: UIScreen.main.bounds)
        window?.rootViewController = navController
        window?.makeKeyAndVisible()

        return true
    }
}

This app uses the Coordinator pattern to separate navigation logic from view controllers. The MainCoordinator manages the navigation stack using a UINavigationController. It starts by showing the HomeViewController, which has a button labeled 'Go to Details'. When the button is tapped, it calls the coordinator's showDetails() method, which pushes the DetailsViewController onto the navigation stack. The back button in the navigation bar is automatically provided by UINavigationController, allowing the user to return to the Home screen. This pattern keeps view controllers simple and focused on UI, while the coordinator handles navigation flow.

Final Result
Completed Screen
┌─────────────────────────┐
│        Home Screen       │
│                         │
│   [Go to Details Button] │
│                         │
└─────────────────────────┘

User taps button ->

┌─────────────────────────┐
│       Details Screen     │
│                         │
│      Details Screen      │
│                         │
│  < Back (Nav Bar Button) │
└─────────────────────────┘
User sees Home screen with a centered button labeled 'Go to Details'.
Tapping the button navigates to Details screen with a label 'Details Screen'.
Navigation bar shows a back button to return to Home screen.
Stretch Goal
Add a third screen called About, accessible from Details screen with a button labeled 'About'. Use the coordinator to navigate to About screen.
💡 Hint
Create AboutViewController with a label. Add a method in MainCoordinator to show About screen. Add a button in DetailsViewController that calls coordinator to navigate.

Practice

(1/5)
1. What is the main purpose of the Coordinator pattern in iOS development?
easy
A. To separate navigation logic from view controllers
B. To manage data persistence
C. To handle network requests
D. To style UI components

Solution

  1. Step 1: Understand the role of Coordinator

    The Coordinator pattern is designed to manage navigation and screen flow in an app.
  2. Step 2: Compare with other options

    Data persistence, network requests, and UI styling are handled by other parts of the app, not the Coordinator.
  3. Final Answer:

    To separate navigation logic from view controllers -> Option A
  4. Quick Check:

    Coordinator = navigation separation [OK]
Hint: Coordinator handles navigation, not data or UI [OK]
Common Mistakes:
  • Confusing Coordinator with data managers
  • Thinking Coordinator styles UI
  • Assuming Coordinator handles network calls
2. Which of the following is the correct way to declare a basic Coordinator protocol in Swift?
easy
A. protocol Coordinator { func start() }
B. class Coordinator { func start() {} }
C. struct Coordinator { var start: () -> Void }
D. enum Coordinator { case start }

Solution

  1. Step 1: Identify protocol syntax

    In Swift, protocols are declared with the protocol keyword and define method signatures without implementation.
  2. Step 2: Check each option

    protocol Coordinator { func start() } correctly declares a protocol with a start() method signature. class Coordinator { func start() {} } is a class, not a protocol. struct Coordinator { var start: () -> Void } is a struct with a closure property, not a protocol. enum Coordinator { case start } is an enum, which is not used for protocols.
  3. Final Answer:

    protocol Coordinator { func start() } -> Option A
  4. Quick Check:

    Protocol syntax = protocol Coordinator { func start() } [OK]
Hint: Protocols use 'protocol' keyword and method signatures only [OK]
Common Mistakes:
  • Using class instead of protocol
  • Adding method implementation in protocol
  • Confusing struct or enum with protocol
3. Given this Coordinator code snippet, what will be printed when appCoordinator.start() is called?
protocol Coordinator {
  func start()
}

class AppCoordinator: Coordinator {
  func start() {
    print("App started")
  }
}

let appCoordinator = AppCoordinator()
appCoordinator.start()
medium
A. start() method not found
B. No output
C. Compilation error
D. App started

Solution

  1. Step 1: Analyze the class and method

    The AppCoordinator class implements the start() method which prints "App started".
  2. Step 2: Check method call

    Calling appCoordinator.start() executes the print statement inside start().
  3. Final Answer:

    App started -> Option D
  4. Quick Check:

    Method call prints message [OK]
Hint: Calling start() runs its print statement [OK]
Common Mistakes:
  • Assuming start() is missing
  • Thinking code won't compile
  • Expecting no output
4. Identify the error in this Coordinator implementation:
protocol Coordinator {
  func start()
}

class MainCoordinator: Coordinator {
  func start() {
    print("Starting main flow")
  }

  func startFlow() {
    print("Flow started")
  }
}
medium
A. startFlow() method is not part of the protocol
B. No error, code is correct
C. Missing start() method implementation
D. Protocol should be a class, not protocol

Solution

  1. Step 1: Check protocol conformance

    The MainCoordinator class implements the required start() method from the Coordinator protocol.
  2. Step 2: Verify additional methods

    Having extra methods like startFlow() is allowed and does not cause errors.
  3. Final Answer:

    No error, code is correct -> Option B
  4. Quick Check:

    Extra methods allowed, required methods implemented [OK]
Hint: Extra methods don't cause errors if required ones exist [OK]
Common Mistakes:
  • Thinking extra methods must be in protocol
  • Assuming missing methods cause errors
  • Confusing protocol with class requirements
5. You want to start a login flow from your MainCoordinator and then show the home screen after login. Which approach best uses the Coordinator pattern to handle this navigation?
hard
A. LoginCoordinator handles login and home screens without notifying MainCoordinator
B. MainCoordinator directly pushes login and home view controllers without coordinators
C. MainCoordinator creates and starts a LoginCoordinator, then listens for login completion to start HomeCoordinator
D. Use a single view controller to manage login and home screens

Solution

  1. Step 1: Understand Coordinator responsibilities

    MainCoordinator should manage the overall flow and delegate specific flows to child coordinators like LoginCoordinator.
  2. Step 2: Evaluate navigation flow

    MainCoordinator creates LoginCoordinator to handle login, then listens for completion to start HomeCoordinator, keeping navigation logic centralized.
  3. Step 3: Compare other options

    The other options break separation of concerns or do not coordinate flows properly.
  4. Final Answer:

    MainCoordinator creates and starts a LoginCoordinator, then listens for login completion to start HomeCoordinator -> Option C
  5. Quick Check:

    Parent coordinator manages child flows [OK]
Hint: Parent coordinator controls child coordinators and flow [OK]
Common Mistakes:
  • Handling navigation only in view controllers
  • Child coordinators acting independently
  • Mixing multiple screens in one view controller
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard