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.
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?
@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.
adding a coordinator property to all view controllers via an extension with get/setAssociatedObject with OBJC_ASSOCIATION_RETAIN_NONATOMIC
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 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
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.
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?
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:
Should the coordinators be reused between application (is it recommended it)?
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
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,
Should the coordinators be reused between application (is it recommended it)?
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.
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