Where to, Coordinator?

Read Time
4 minute read

In mobile, every application that doesn’t consist of only one single UI, will need some sort of navigation. This enables a user to transition between different screens based on events or user interaction. In the AJ Bell application, we rely heavily on a “Navigation Stack”. A navigation stack refers to the way in which screens or pages of an app are organized and navigated between. The stack is a data structure that holds the history of screens or pages that the user has visited, with the most recent screen at the top of the stack. When a user navigates to a new screen, it is pushed onto the stack, and when the user navigates back, the current screen is popped off the stack. Navigation stacks are commonly used in mobile app development to provide a smooth and intuitive experience for users when navigating through the app.

Whether we use the traditional pop and push methods, or even using modal screens, getting a consistent and an easily maintainable navigation architecture in place can be quite tricky. The trap that we tend to fall into is that each screen could be tightly coupled to another screen.

The Pushing and Popping method

One of the most common ways of displaying a different screen is to “push” another screen onto the navigation stack. This means that the view you are currently on, needs to know about the next screen to be pushed on. It shouldn’t really know about it, and if you think about it carefully, it goes against the Single Responsibility principle in the SOLID principles.

Take a look at the iOS using Swift example below:

An example of the Pushing and Popping method on Swift

In this example, we want to push the Settings screen on from the More Options screen, from inside the More Options screen itself. This does work fine, but it could cause an issue in the future, if we wanted to change this navigation around, or put another screen in the middle of these.

Coordinators

Using a Coordinator based architecture is becoming more and more popular in the mobile world, and they really take away any navigation logic from the views themselves. Their sole purpose is to decide the navigation between screens.

Say for example we want to introduce a flow that would allow a user to add an additional account. Depending on the account type we could have several screens. Imagine if every single screen needed to know about the next screen it needs to present. It could get quite messy, and hard to maintain, couldn’t it? Furthermore, if we ever wanted to change the flow of this feature, it would require a lot of refactoring.

We can introduce a coordinator, say for example an AddAdditionalAccountCoordinator, that takes care of all the navigation between the different screens. The first step is to get the View Controller’s View Model, to notify the coordinator that the user wants to proceed to the next screen - the view model’s job is now done:

The example using Coordinator based architecture on Swift

The Coordinator now receives this message, and it can then decide what it wants to do next, such as presenting the next screen, and in this example, to present the “Check My Details” screen.

The Coordinator based architecture on Swift

It would then be so simple for us to change the flow if required. We can swap out the showCheckMyDetailsScreen() call, to another function call, such as showBankDetailsScreen() and it would be that simple.

Conclusion

Moving navigation logic outside of views and view controllers into coordinators can make transitioning between multiple screens a lot simpler and cleaner. What is great about coordinators is that they are highly composable, meaning that we can separate our navigation logic into smaller, more manageable components.

By Adam, IOS Mobile Engineer III