Coordinators

Bruno Rovea
8 min readJan 29, 2019

--

How to route between the application's flows

First of all I have to say that this article was inspired by many other articles that made me learn how to use Coordinators, along with the experience of using this pattern to route the applications I work with.

Routing is some of the basic functions you should do to build an app. The complexity starts low, but increase with time when you need new scenes, new flows and support new business logic. Along with that you try to make your app scalable using OO and SOLID concepts to make it readable, reusable, testable and maintainable.

So what should you use to route your app and help you accomplish it? Coordinators!

In this article I'll try not to deep dive in code, trying to show the minimum for you to understand and build it yourself.

Coordinator?

So… what's a Coordinator? Well, a Coordinator is… a coordinator. Its purpose is to coordinate where and how your app should navigate, to and from a scene, if it's in the same flow or starting a new flow, based on business logic. If you don't know what a flow is, it's a concept of telling the USER a STORY, let's say, the authentication flow, the profile flow, the payment flow, the statement flow and so on. A flow is composed by a collections of screens, which we'll refer as scenes.

Let's think for one moment when you want to move from one point to another in your city. You take your car, take the first right on the Avenue Splash Scene, take the second left on the Highway Home Scene, there you pass through some possible exits, but you want to enter the Profile Scene Street, where you have a toll that asks for something in exchange to let you in, that's exactly what you will build when using Coordinators (Well not exactly because it won't be a traffic system).

Remember that a flow can be either horizontal or vertical.
Horizontal flow: When you push a ViewController using a NavigationController.
Vertical flow: When you present a ViewController.
Note: Even when you present a NavigationController and start using push, it's still a vertical flow.

Normally, how do we navigate between scenes and flows?

Well, everyone has seen all kind of code to push or present a ViewController. From my experience, I have seen:

switch NavigationOptions {
case .Segue: Probably the first mode everyone used when started developing for iOS. It has serious limitations when navigating and lots of flaws to control flows. It should always be avoided when possible.
case .Directly: Normally after you learn the use of Segues limitations, you start using push and present directly from the ViewController. It too has limitations when navigating, but supports initializing a ViewController that is not in the Storyboard, which is nice. This option is OK when you have an application with just a handful of scenes, because it's easy to lose yourself when routing.

case .Router: For the last years, at least to me, Routers earned its space, but normally people misuse them, because they don't control the flows inside the Routers and make the routing from the ViewControllers, sometimes from ViewModels, and believe it, even from Services classes. This option is also OK when you have an application with just a handful of scenes, but also can be very powerful if used correctly.
}

But, what if you have more than a handful of scenes? What if you have more than a couple of flows? What if the business logic and the flows are always changing because your UX is experimenting some A/B tests, or the project's manager said so or well, because yes.

That would be a headache for the developer to keep changing the code and rearrange the transitions between scenes and flows.

So, how and when should you use Coordinators?

Which presentation architecture should you choose to use Coordinators? Well, for what I have seen, any architecture is compatible with Coordinators, but from my experience and the use cases I had, it goes really well with MVVM, showing a powerful and, at the same time, simple solution to almost any application.

There are lots of ways to use Coordinators, below I'll show how I use it and try my best to show other ways to use them.

Remember that you don't need a bazooka to kill a fly. If you have a simple app with a handful of scenes, not many business logic and it probably won't change so soon, feel free to use segues, navigate directly to the scenes or even use routers (properly), but remember to finish the article and consider the Coordinator pattern an option too :)

Building the Coordinator

So let's get started. First of all, all Coordinators should conform to the Coordinator protocol which consists of just one function:

You will call this functions after you have initialized the Coordinator and the business logic is ready to start the new flow.

But you may ask, what else do I have to put in a Coordinator for it to start working properly? Well, for my use cases, I normally use from 2 to 5 scenes on each flow, but it could be from 1 to n scenes, so to present the first scene, it would look something like this:

In the example above, which we'll refer as the child Coordinator, we have the presenter which should be the parent Coordinator's scene and the ViewController that will make the transition to the child Coordinator and start the new flow. Inside the start function we create the first ViewController from the child Coordinator flow, make the DI and present it.

Looks great and simple, right?

Navigating to a new scene

That's nice and all, but how can I navigate to the second scene based on a business logic? Probably you'll have an action to trigger it, let's say an @IBAction and make the navigation logic there.

When you use Coordinators along with MVVM, the ViewController shouldn't know how to navigate, it should just send the user actions to the ViewModel, so the ViewModel will know what to do with the actions, that's why we put the navigationDelegate in the ViewModel in the example above.

The ViewController code should look like this:

And the ViewModel code should look like this:

Now we extend the functionality of our Coordinator to handle the navigation protocol:

Note that the function onNavigate could contain any parameters you would like to send to the next ViewController, injecting it in the ViewModel through DI.

Nice, right?

Extension Navigating

If you noticed, there's a mistake in the example above, because we expected an UIViewController as the presenter and then called push in the onNavigate function without referencing the possible embedded navigationController. So, how do we solve this? It depends!

Let's say you have a flow which you want to continue with a new Coordinator, but you want your user to feel that it haven't changed and use push to transition into it. You can send the current UINavigationController as the new Coordinator presenter, and then change from present to push the FirstViewController in the start function.

Now let's think the other way around and you want your user to feel that he changed flows and present the next ViewController. As I stated before, you can inject a new UINavigationController (if the new flow has a horizontal navigation inside it) or create one inside the new Coordinator, which I prefer. Take a look in the code below.

Now instead of the presenter you can use the private navigationController to push new ViewControllers in the flow and use it to dismiss the flow too.

Navigating to a new Coordinator (flow)

Navigating inside a Coordinator is pretty easy, right? But what about navigating to another Coordinator? Well, it's easier!

Take a look at the code below, what do you think?

We just need to save the reference of the new Coordinator, inside an array of child Coordinators that is in the current Coordinator so it won't be able automatically deallocated by ARC. If you're asking yourself about how you are going to dealloc it, remember on the previous topic that we said about dismissing the flow?

You said dismiss? How about finishing a flow?

So far so good, but how can we tell that a child Coordinator finished its flow to the parent Coordinator? And what if it needs to delivery something to the parent Coordinator to make it update itself or do something else?

Well, you can use the delegate or the closure pattern protocols.

The delegate protocol needs to be implemented by the parent Coordinator, while the closure protocol needs to be implemented by the child Coordinator.

When finishing don't forget to dealloc the child Coordinator you just dismissed. Below is the example with the delegation pattern.

Just remember that we choose to dismiss or pop a child Coordinator inside itself using the presenter or the private navigationController reference. I have seen some people that chooses to dismiss the child Coordinator from the parent Coordinator, and that is up to you.

And what about the initial flow (aka AppDelegate flow)?

Normally we create the ApplicationCoordinator, and use it in the AppDelegate. There you can make the initial flow when your app just finished launching, when received a push notification or even when it is started by a deep link.

So you may ask, what would be the presenter in that case? Well, there are cases where people create a NavigationController in the AppDelegate and cases where people send the UIWindow as the ApplicationCoordinator presenter. I prefer the latter.

An example of an AppDelegate would look like this:

It's important to have a reference for the Application Coordinator, so it won't be deallocated and you can easily execute another flow if you receive a push notification or a deep link, or any other business rule.

It's inside the ApplicationCoordinator where we normally change the flow when it's necessary to change the UIWindow's root view controller. If you don't know what the UIWindow's root view controller is, it's the main ViewController of the current UIWindow, the one you probably used in the AppDelegate. If it still doesn't make sense, I advise you to search for it, because it's very important for apps with more than one flow that need to change it based on a business rule.

OK, I'm already tired of Coordinators. Is there anything else?

Actually there is:
1. Sometimes you need to handle the UINavigationController back button and gestures actions to trigger something or even dealloc a Coordinator which was pushed(I'll update the article with it in the future, but you can take a look at UINavigationControllerDelegate to try one of the possibilities)
2. You can use a Reactive approach where you could decrease or even eliminate the delegation of hell that it may build with so many business' logic.
3. You could supercharge your Coordinators with Routers inside them for an even more powerful navigation framework!

We won't talk about it for now because I think we've covered most cases you would need to support small to medium complexity applications.

Of course some applications may have some business logic, flows, or any other necessities that Coordinators may not work properly with, but I think there won't be many cases.

Just remember that when you have a specialized class to be responsible for a part/function of your application, like the navigation part, you are one step closer to build a clean architecture and make use of some buzzwords, like SOLID, DRY, SSoT, KISS and many other, that sometimes sounds irritating and we don't care much about, but are really important when building a readable, reusable, testable and maintainable app.

Now I should apologize because I said I wouldn't show much code, but while writing I thought it would be easier seeing it implemented than just explaining with my own words :)

Any questions, comments, errors, improvements? Feel free to point it.

Thanks for the reading!

--

--