top of page

Build Scalable Multi-Flow iOS Apps with Coordinators, MVVM, and SwiftUI

Writer's picture: Eric PalmaEric Palma

Updated: Dec 31, 2024

In the previous post, we introduced the Coordinator pattern and showed how to implement a simple home-to-detail navigation flow using UIKit for navigation and SwiftUI for views.

But real-world apps often have multiple user flows, each with its own set of screens.


To make navigation scalable and maintainable, we can use sub-coordinators—a perfect fit for complex navigation requirements. In this step-by-step guide, we’ll build a more advanced example: a multi-flow app with a registration flow and main app flow.


What We’re Building

We’ll implement two separate navigation flows managed by sub-coordinators:

  1. Registration Flow: A sequence of onboarding screens:

    • Sign Up

    • Profile Setup

    • Welcome

  2. Main App Flow: A standard list-to-detail navigation:

    • Home Screen

    • Detail Screen


These flows will be managed by a RootCoordinator, which determines where the user starts and switches between flows when needed.

Here is a basic demonstration of the entire flow in action:




Why Sub-Coordinators?


When apps grow, navigation logic can quickly get out of hand. Sub-coordinators allow us to:

  • Isolate the navigation logic for specific user flows (e.g., registration vs main app).

  • Improve maintainability by enforcing separation of concerns.

  • Easily reuse and test flows independently.


The Root Coordinator


We will begin at the highest level and dig down as we progress. We will have a RootCoordinator which will be solely responsible for moving users between two flows: from the registration flow to the main app flow.


It will conform to the Coordinator protocol we introduced in the previous post: Intro to the Coordinator pattern with SwiftUI and UIKit. You can find the definition below:

Things to note:

  1. Our RootCoordinator holds references to it's sub-coordinators.

  2. All the coordinators share a reference to the UINavigationController, which they use to manipulate the navigation stack.

  3. When our app starts, it starts the correct user flow based on one condition, whether the user already registered or not.


Since we are performing programmatic navigation using UINavigationController, we have plenty of flexibility in terms of manipulating the navigation stack. Notice that when we start the main app flow, we clear the stack so that the main app flow begins with a clean slate. Otherwise it would be possible for users to “go back” to the registration screens even after they completed that flow, that is not good user experience (UX) and leads to confusion.


The Sub-Coordinators: RegistrationCoordinator and MainAppCoordinator


Our RootCoordinator is composed of two sub-coordinators responsible for their own user flows. 


      Want to read more?

      Subscribe to curiousalgorithm.com to keep reading this exclusive post.

      bottom of page