This post will introduce the concept of coordinators, and how they can be used beneficially in an iOS application. This post is part of a series of posts about iOS architectures, if you are interested in reading more about additional architectural patterns, check out my articles about “VIPER” and about “MVVM”. The coordinator pattern works great in combination with the MVVM architecture.
The coordinator pattern introduces a new component to an iOS project - the coordinator. The core responsibility of a coordinator is to handle the application’s view creation and the flow logic between the app’s different screens.
A common issue in an iOS project, using the MVC pattern, is that the Controller is both responsible for handling its own View - by implementing a View’s delegate and data source methods - as well as creating other Views and handle the routing logic between these different Views and ViewControllers. This makes it difficult to reuse a ViewController in a different flow. This also contributes to code entanglement - making it difficult to read the code, to test the app’s behavior and to follow the single responsibility principle. This is where the coordinators can be really helpful.
How do the Coordinators work?
A coordinator is responsible for creating a specific screen in our app. To do this, the coordinator will first take the responsibility of retrieving all the dependencies that are required for the specific screen that we want to display - this includes retrieving the Models that we want to display in a screen. Secondly, the coordinator will initialize the ViewController that we need for the specific screen, when initializing the ViewController, the coordinator will use dependency injection to provide the ViewController with its dependencies. Once this ViewController has been initialized, the coordinator will present the newly created ViewController.
Above I just described how a coordinator is responsible for a specific screen in our application. There are also other benefits and use cases of coordinators; in an app, we can have a main coordinator often referred to as the AppCoordinator. This AppCoordinator is responsible for creating our initial screen and can be really useful for a more complex app which contains multiple screens, which each have different navigation flows. An example of where the AppCoordinator provides us with benefits is an app that contains a tab bar, with different screens for each tab, which all contain their own navigation flow logic. An AppCoordinator is our initial coordinator, which is often initialized by the app’s AppDelegate class. This AppCoordinator is responsible for creating ChildCoordinators - which are the coordinators that are responsible for the creation and the navigation for a specific screen, as described above.
Using coordinators to improve the structure of our ViewController
In the section above I have described how a coordinator is used for initializing our main screens and ViewControllers in a tab bar. Using coordinators can also help us to move navigation logic out of our ViewControllers.
For example, if we have a View which contains a button which should trigger the navigation to a different view when it’s tapped, our ViewController will need to retrieve the dependencies for this new View and its ViewController, then it needs to create the ViewController, and then present the new ViewController. If we have a coordinator which is responsible for creating this initial ViewController, we can also control the ViewController’s navigation behavior for when the button is tapped from our coordinator. This is done by defining the navigation logic and the behavior of the button tap in our coordinator. Once this logic is defined, we can either assign the coordinator to be the delegate of the ViewController, or we can pass our button’s navigation functionality in as a parameter to the ViewController on its initialization, this passed in functionality can then be utilized by the ViewController.
The benefits of the coordinators
Introducing coordinators to our iOS architecture gives us several benefits.
First of all, since a coordinator takes on the responsibility of creating ViewControllers, creating their dependencies, and are responsible for presenting these ViewControllers, we are able to consolidate the dependency retrieval and the initialization of a new view in one place.
A coordinator which is responsible for the navigation logic to a specific ViewController in our app can also handle navigation from this ViewController to another view. This means that the logic of navigating from one ViewController to an other, which used to live in the ViewController is being handled by our coordinator instead. This is specifically beneficial when our navigation logic is more complex and if it depends on a state; for example, a user’s login state.
Separating the navigation logic away from the ViewController also makes our ViewController more reusable. We may want to reuse a ViewController which contains a button, but in one use case we want the button to lead to one view, and in a different use case, we may want to display a different view when the button is tapped. By separating out our navigation logic into the coordinator our ViewController does not have to contain any case validation to handle the button tap, instead, we can have two different coordinators for the two instances of the ViewController, each containing their own, separate navigation logic while reusing one ViewController.
Apart from moving code out of our ViewControllers and consolidating our navigation logic, the coordinators also contribute to a more light weight AppDelegate. Earlier in this post, I describe the responsibility of an overarching AppCoordinator and its initialization of its ChildCoordinators for an app with a tab bar. The logic of creating the different ViewControllers for a tab bar would usually be contained in the AppDelegate, when using the coordinator pattern we can instead consolidate the creation of our ViewController’s and the presentation of the ViewController’s within one dedicated class.
Soroush Khanlou has some great posts about the coordinator pattern, if you want to read more in-depth information about this pattern, I would recommend he’s “Coordinators Redux” post.