Kodeco Forums

Coordinator Tutorial for iOS: Getting Started

In this Coordinator tutorial you'll convert an iOS app from using the MVC pattern to the Coordinator pattern and examine the pros and cons of Coordinators.


This is a companion discussion topic for the original entry at https://www.raywenderlich.com/158-coordinator-tutorial-for-ios-getting-started

Please correct me if i am wrong, but donā€™t you have a memory issue here? By having each coordinator retain itā€™s following coordinator (and not cleaning it up after), wonā€™t every screen still be alive in memory once you reach the details screen and press the back button back to the root view controller?

@haawa Can you please help with this when you get a chance? Thank you - much appreciated! :]

@rockbruno Thats a really good question. In this tutorial I simply omitted memory question to keep it simpler. With current implementation there is a problem with retaining child coordinators.

Ideally child coordinators should notify parent through delegate that they finished their task, and then parent coordinator should release this child. With navigation controller as a root, itā€™s a bit trickier. Possible solution would be for parent coordinator to listen to navigation controller delegate and based on navigation release itā€™s children.

@rockbruno brings up a good point, but I donā€™t think monitoring the navigation controller is good enough as some presentations might not be suitable for such a setup.

First of all, I would break the strong reference between the child coordinator and the parent coordinator and store it with weak.

Secondly, I would keep the coordinator alive with a strong reference from the controller it presented, this would give us the immediate benefit of freeing memory as soon as the view controller is deallocated.

The downside that I see is that at that point the controller will ā€œknowā€ about its coordinator, which is not ideal.

Yeah I totally agree, but I would argue that having view controller knowing about coordinators is conceptually worse then tracking navigation stack.

With this pattern view controllers are reusable components, which theoretically could be copied and reused in your other projects, which are not always built with coordinators.

The way I see this working is

  1. adding a coordinator property to all view controllers via an extension with get/setAssociatedObject with OBJC_ASSOCIATION_RETAIN_NONATOMIC

  2. the view controller per se has no business ever touching (or knowing of) its coordinator property so controller reuse would still be possible

The only purpose of this mechanism would be to keep the coordinator alive for the duration the view controller is in use.

Follow up question: what is the benefit to keeping a reference (weak or strong) to child coordinators in a parent coordinator? Their use seems to be ā€œfire and forgetā€, do you have an example in mind where we would need to use the reference to the child coordinator to handle something?

@clawoo The reference is there for parent coordinator to have control over itā€™s children. For example:
authentication token expired and the user needs to be logged out. This event is an application level event, and ApplicationCoordinator receive a callback. Application coordinator dismisses itā€™s currently displayed child, and creates and starts LogInCoordinator.

So basically itā€™s parent coordinatorā€™s job to switch between itā€™s children, to maybe pass some events or even data to them etc.

I want you to understand that there is no correct answer to this problem, and your suggestion is absolutely valid and it definitely has itā€™s pros. I would still choose to handle dismissal by myself instead of leaving this to UIKit, which is closer to how it is in MVC.

The beauty of the coordinator patter is that it gives you room to implement it in whatever way you see fit, and forces you to think and made decisions as an app architect. I encourage you to try it either way in your apps and to decide which seems better.

I asked that because we had the same issue when we first developed our app, and it sparkled a few days of brainstorming on how to fix it :slight_smile: At the end, we managed to develop a smart system with zero memory issues (for nowā€¦). Our coordinators donā€™t retain their childs, instead, the childs have a weak reference to their parents.

Every ViewController is a CoordenableViewController which overrides didMoveToParent to call a releaseCoordinator() method whenever the view doesnā€™t have a parent anymore (a.k.a: back button pressed). The real retaining happens at a special guy called NavigationCoordinator, which is the same thing as a Coordinator except it maintains a navigation controller with a dictionary that maps viewcontrollers to their respective coordinators. Every push/pop (forced dismissal)/release (back button dismissal) gets routed to him (by tracing back the parentCoordinators that each coordinator has), so he deals with everyone in theory. Thereā€™s special treatment for modal presentations, but for push/pops itā€™s pretty straightforward.

The more you want to cover, the more complex it getsā€¦ But I think itā€™s a decent trade for what this architecture has to offer :slight_smile:

Isnā€™t Coordinator same as Router component in VIPER architecture?

Good tutorial. Thanks for sharing. The coordinator pattern is something Iā€™ve used in my projects for a while now. Iā€™ve found any other architecture than MVC for Cocoa development to have some kind of drawback; or I end up fighting against Appleā€™s frameworks. But MVC-C seems to work well.

From my experience, I see coordinators as part of the ā€˜controller layerā€™ of MVC. Theyā€™re able to take away most of the responsibilities that we usually lump with view controllers - which often leads to their massiveness.

A slight variation Iā€™ve used that differs from the approach in this post is not to create a delegate between the view controller and itā€™s coordinator. Instead, every action/event which the view controller handles involves sending a message to the model layer (or ā€˜storageā€™ in your example). This allows the model layer to handle ALL the business logic - even if it involves simply navigating to another view controller. The model might want to do things such as make calls to analytics services or carry other non-UI activities.

The way the model then communicates with the coordinator is the same way it communicates with the view controller - notifications. Yes, notifications seem a little clunky and outdated in 2018 but theyā€™re still the suggested way (by Apple) for model layers to communicate with controller layers.

One role I assign to a ā€˜sceneā€™sā€™ coordinator is to setup all the necessary notification subscriptions for both itself and the view controller.

All the above, Iā€™ve found, leads to much more sane view controller code that focuses on what view controller code should be mostly doing - controlling views.

Great tutorial, thank you.

I donā€™t get this part though:

class KanjiDetailViewController: UIViewController {
   let viewController = KanjiDetailViewController.instantiateViewController()
   // keep the other code within this class below
}

Why would the controller have a property where it instantiates itself? Or is this an error and this property should be in the coordinator?

Thanks.

This is an error, it should be a coordinator. I will fix this when I return from vacation. Thanks for pointing out!

@haawa I have the following doubt:

I started implementing the coordinator pattern. The project consist on three applications (but to simplify letā€™s said 2ā€¦until now, only one was developed), now weā€™re thinking on starting the next project and reuse many things that have in common (shared framework).

First, I have a general question:

  1. Should the coordinators be reused between application (is it recommended it)?
  2. Is ok for the coordinators to end up calling Networking logic?

Let me put the following example:

Both application (from now on App1 and App2) has a settings screen (almost identically on some option, but App1 has more options ~ Itā€™s a tableView, with three type of cells in common, but App1 has an additional type cell).

Right now i have 2 options in mind, but itā€™s basically a trade off between readability vs reusability:

Option #1: (Favor Reusability)
I could reuse the UITableViewDataSource & UITableViewDelegate & SettingsViewController, basically creating a SettingsSection and SettingsCell (will contain a type and closure to call when the cell is selected) model. Each application will have itā€™s own SettingsCoordinator and will configure a datasource object with the SettingSections (and SettingsCell). I guess is normal for the coordinator to setup the dependency of the controller, but then the logic when the cell is selected will be in the coordinator (ex. if a cell is selected, and that cell needs to logout the user, will need to call the logout endpoint from the coordinator, is that ok?).

Option #2: (Favor Readability)
I can have a BaseSettingsViewController (to move common navigation callbacks there), and both application (targets) will have itā€™s own SettingsViewController, which will implement itā€™s own datasource & delegate (plus adds itā€™s specific callbacks), also each app will have itā€™s own coordinator (with specific navigation logic, although can add a BaseSettingsCoordinator to have the common logic). In this way Networking calls can be add directly on SettingViewController.

Just one to know other opinions and will be great to see how you would tackle this problem,
Thanks in advance

@haawa Can you please help with this when you get a chance? Thank you - much appreciated! :]

You still on vacation? :wink:

Iā€™m back! Thanks for reminding, Iā€™ve updated the code to Swift 4.1 and fixed the error that youā€™ve spotted.

As for your questions,

  1. Should the coordinators be reused between application (is it recommended it)?
  2. Is ok for the coordinators to end up calling Networking logic?

1: I would say that coordinators should not be reused between apps.
2: Ideally the coordinator should not trigger networking logic. I would create a separate entity for this.

But take it with a grain of salt, there is no ā€œcorrectā€ way to implement this pattern. And you can tweak it to your needs.

As for approach to building apps, from what Iā€™ve read, I would go with the second option.

As mentioned above, I believe this example has several memory issues need to address.

(1) Only whoever coordinator creates the UINavigationController should own it (ApplicationCoordinator in this case). Other coordinators should use unowned let.
(2) Tapping the built-in navigation Back button to pop the view controller will result in memory leak because coordinators are still strongly referenced by the parent coordinator, and hence the popped controller is strongly referenced. My solution is using UINavigationControllerDelegate.navigationController(_:didShow:animated:) to be notified when navigation pop happens, and then perform cleanup.

Along with that, I think we donā€™t have to have a start because Khanlou or someone else started it. I consider lazy var as a better approach. With lazy var, view controller in coordinator doesnā€™t need to be optional.

And I think one coordinator should have only one view controller, otherwise it defeats the purpose of using coordinator for clean code. kanjiDetailViewControllerDidSelectWord should talk to a coordinator of KanjiListViewController, otherwise KanjiListViewController cannot be tested without interacting with KanjiDetailCoordinator first.

@haawa Do you have any feedback regarding this? Thank you - much appreciated! :]

Hi
I am suffering massive memory management problems with this approach. I get the flexibility of presenting view controller of my choice but as a con my view controller stack keeps on increasing and ultimately application crashes / is closed by the system. Please publish a version of this which clears memory management issues else all these tutorials just become fancy toys but not to be applied in real life.
Thanks & Regards
Guru