top of page

Intro to Coordinator pattern with SwiftUI and UIKit

Writer's picture: Eric PalmaEric Palma

Updated: Jan 11

When building complex user navigation flows, separating navigation logic from views is essential for scalable and maintainable code. Enter the Coordinator pattern —a design pattern dedicated to orchestrating navigation between a series of views and potentially other coordinators, ensuring a seamless user experience.

This is an introductory article to the Coordinator pattern using UIKit and SwiftUI, in our subsequent post we use the coordinator pattern to build a more advanced user flow, see: Building a multi-flow iOS app using the Coordinator pattern.

What’s Covered:


  • Introduction to the Coordinator Pattern:Learn the fundamentals of the Coordinator pattern and how it separates navigation logic from views for scalable, maintainable code.

  • The Power of UIKit and SwiftUI Together:Discover why using UIKit for navigation and SwiftUI for views creates a robust and flexible implementation of the pattern.

  • Step-by-Step Implementation:Walk through creating a simple home-to-detail navigation flow, leveraging UINavigationController and UIHostingController.

  • Xcode Setup for the Coordinator Pattern:Configure your project for programmatic navigation, including removing storyboard dependencies in Xcode 15+.


What You’ll Learn:


  • How to use the Coordinator pattern to organize and simplify navigation in your iOS apps.

  • Techniques for combining UIKit’s robust navigation with SwiftUI’s flexibility and speed.

  • Step-by-step code examples to implement and customize navigation flows.


The Best of Both Worlds: UIKit and SwiftUI


While the Coordinator pattern can technically be implemented using SwiftUI, it’s important to acknowledge that SwiftUI navigation is still evolving. It currently lacks some of the flexibility and robustness that UIKit navigation offers.


Thanks to UIHostingController, we can embed SwiftUI views within UIKit’s navigation stack. This allows us to take full advantage of UIKit’s mature and customizable navigation APIs while still leveraging SwiftUI’s declarative and lightweight approach for building individual screens.


This harmonious integration not only enhances user navigation but also streamlines the development process, making it a truly win-win scenario for developers and users alike. Before exploring how the Coordinator pattern is implemented using UIKit and SwiftUI let's understand what it is to begin with.


How does the Coordinator pattern work?


Each coordinator object is in charge of a flow, in this instance, a flow refers to the set of screens that a user can move between in a particular order. A simple example would be a user sign-up flow: The user starts at a sign up screen, then they move to a welcome screen, then finally they move to the home screen.

Another common example is a list to detail flow: The user views a list of items, when they click on an item, they are navigated to a detail screen. The Coordinator pattern supports navigating back as well, so it supports taking the user from a detail screen back to the list screen.

In both of the above examples, the coordinator is in charge of moving the user through the screens, maintaining any necessary state or passing data between screens.


To learn more about data flow in SwiftUI apps, check out this post: Data Flow & Data Sharing using SwiftUI

Coordinators are not limited to only managing screens, they can also contain other coordinators that represent more user flows. For example, we could have a RootCoordinator that manages a RegistrationCoordinator and a ViewItemsCoordinator: when the user completes the user registration flow, they move to the view items flow.

In this case the RootCoordinator only manages transitions between user flows (registration and view items), while the child coordinators only worry about their own user flows and screens. This enforces separation of concerns by forcing each coordinator to only concern itself with it's own screens and flow.

Boost your iOS dev journey by unlocking exclusive in-depth posts, guides, and downloadable code! Accelerate your growth!

Basic implementation of the coordinator pattern using UIKit and SwiftUI


We are now ready to walk through a simple example where we implement a home screen to detail screen flow. To start, we will define a Coordinator protocol which every coordinator object will conform to:

With the above protocol in place, we can now define our app's coordinator, AppCoordinator, which will be responsible for navigating users between the home screen and a detail screen. Below is the code for our AppCoordinator:

In the above code it is important to note the following:

  1. Our navigation stack uses the UIKit based UINavigationController.

  2. SwiftUI views are wrapped in UIHostingController in order to use them with UIKit objects such as UINavigationController in this case.

  3. Once our SwiftUI views are wrapped in hosting controllers, they can be added to the navigation stack.

  4. Our view models get a reference to the coordinator object.


Following the MVVM pattern, our view models would be the ones injected with a Coordinator instance, which they will use to trigger navigation actions. Below are the implementations for our HomeViewModel and DetailViewModel:


To learn about dependency injection in SwiftUI apps, see: Understanding Dependency Injection in SwiftUI Apps: A Step-by-Step Approach

The last thing left to implement are the SwiftUI views, which we can implement in our usual way, without worrying about how we are going to navigate. Most importantly, without needing to rely on SwiftUI navigation APIs. Below are the HomeView and DetailView implementations:

Setting up your Xcode Project for the Coordinator pattern


If you’re using Xcode 15 or later, you’ll notice that you can only select Storyboard or SwiftUI as the interface when creating a new project. Since the Coordinator pattern relies on programmatic navigation with UINavigationController, you’ll need to manually configure your project to remove storyboard dependencies and set up UIKit properly.


Here's how:


1. Choose “Storyboard” as the Interface


When starting a new project, select Storyboard as the interface option. While this may seem counterintuitive, it ensures Xcode sets up the necessary AppDelegate and SceneDelegate files, which are essential for UIKit-based navigation.


2. Remove Storyboard References

To work with the Coordinator pattern programmatically, you’ll need to remove the storyboard dependency:


  1. Delete the Main.storyboard File:

    • In the project navigator, locate Main.storyboard.

    • Delete the file (right-click → Delete → Move to Trash).

  2. Remove Storyboard from Info.plist:

    • Open Info.plist in your project.

    • Find Application Scene Manifest.

    • Nested under it, locate the key StoryboardName and delete it entirely.

  3. Remove Storyboard references from Project:

    • Go to your project settings.

    • Select your target → Info tab.

    • Nested under Application Scene Manifest, find:

      • Find the key Main storyboard file base name and remove it.  

      • Find the key StoryboardName and delete it entirely.


3. Set Up Programmatic Navigation in SceneDelegate or AppDelegate


With the storyboard removed, you can now configure navigation programmatically using a UINavigationController and your AppCoordinator.


For iOS 13 and Later (SceneDelegate):


In SceneDelegate.swift, initialize the AppCoordinator:


4. Proceed with the Coordinator Pattern Implementation


With your project now set up for programmatic navigation, you’re ready to implement the Coordinator pattern. Your AppCoordinator will take control of the navigation stack, managing transitions between flows and embedding SwiftUI views via UIHostingController.


Recap


In this post we merely got a high-level introduction to the Coordinator pattern for navigating between screens in iOS apps. We learned how the coordinator pattern moves navigation logic out of the views and view models and makes our navigation code scalable.


There is so much more we can do with coordinators and I will demonstrate doing that by building a more complex application in the following post, so make sure to join our newsletter, or better yet, become a member of our site!

Site
Join the growing community of iOS engineers who are taking their skills to the next level.

© 2025 Curious Algorithm. All rights reserved.

bottom of page