The C in MVVM-C.
This article is about iOS Development and features examples using MVVM and Reactive Programming (ReactiveKit).
The Coordinator pattern was first introduced by Soroush Khanlou at NSSpain ‘15. In the next few paragraphs you will find out how to incorporate it into your app as the missing piece of the MVVM pattern.
MVVM and its shortcoming
Model-View-ViewModel is a commonly used and well-known architectural pattern in the iOS world, offering you a handy toolkit to build decoupled, well-structured code.
If you are not familiar with it, you can watch Ray Wenderlich’s walkthrough.
You’ve most certainly read a thousand times about the Massive View Controller and how MVVM solves that problem. However, the latter isn’t perfect in every case either.
The Problem
So what’s the issue? To put in fewest words possible: Segues force view controllers to be dependent on each other.
Let’s take a look at a simple example.
This is a fairly simple Login flow that allows you to enter your credentials or have a look at what the app has to offer first, via the Onboarding screen. Let’s say further down the road you decide to have a similar screen that will act as a walkthrough for a new feature you’ve just introduced. In the current implementation, the OnboardingViewController
is tightly coupled with the LoginViewController
and it is only supposed to be invoked by a segue.
The solution
First of all, it’s a good idea to stop using storyboard segues for screens that you would want to be reusable. Or just don’t use storyboard segues at all. You can either write all your view controllers programmatically, or make separate storyboards for every user story / screen.
With that being said and done ..
Enter: The Coordinator
The almighty Coordinator steps in to take the sole responsibility of navigation flow, so that your view controllers are no longer bothered by it.
Whenever there’s a need to transition to the next screen, the corresponding coordinator is notified by the viewModel.
Let’s build that Coordinator.
Coordinator Protocol
The protocol is straight-forward. Every Coordinator
must have a context
in which it operates on and a navigationController
that it will use to present the view controllers. The push(_:)
and present(_:)
are just helper methods - we also provide a default implementation of those.
The start()
method is intended to be used as a point of initiation of the flow.
Overview
Nothing interesting here. Let’s jump straight into the OverviewCoordinator
.
Overview Coordinator
⚠️ In order for instantiateViewController(withIdentifier:) to work properly, you must specify
Storyboard ID
for every view controller in a storyboard. ⚠️
We already see how the issue from earlier is resolved. The OnboardingViewController/TutorialViewController
that was previously married to the LoginViewController
is nicely isolated and fully reusable. A strong and independent view controller.
Showing Details Screen
Let’s go back to the Overview Screen and see how we will handle tapping on a certain item and going into the Details Screen.
Step 0: tableView(_:didSelectRowAt:)
:
Step 1: Bind
⚠️ If you are not using ReactiveKit / RxSwift, you can use the Delegate pattern to notify the Coordinator when it should show a screen. ⚠️
Pretty simple, right?
Coordinate the Coordinator
The concept behind coordinators should be pretty clear by now. One of the things you may still wonder about is who takes care of the coordinators? Well.. other coordinators.
App Coordinator
If you want to fully take advantage of the pattern, you have to use it all over the application you’re building.
The App Coordinator manages the intial flow and gets initiated within the App Delegate.
Declaration
Usage in AppDelegate
And that’s pretty much the foundation of it.
Conclusion
A Coordinator is an object that:
- Manages the flow of the app / particular user story.
- Creates View Controllers and presents them.
- Creates child Coordinators and starts them.
- Fully customizable to fit the requirements of the app.
—
The Coordinator pattern allows you to isolate your View Controllers so that they are highly reusable and decoupled. It makes it a thousand times easier to navigate to the same controller from multiple places or even start your app flow from a diffent screen (in case it was opened from a Quick Action, Deep Link, Notification, etc.). You are also forced to better structure your code and ditch enormous storyboards. As a result you get highly scalable, easily maintainable project.
Here you can watch the NSSpain ’15 talk where the Coordinator concept was originally introduced.
— Thank you for reading this post. ✌️