How to use the Coordinator pattern in iOS
Vložit
- čas přidán 26. 08. 2024
- The coordinator pattern takes responsibility for navigation out of your view controllers and into a separate class, helping you make your view controllers smaller and simpler.
In this video I'm going to explain the problem coordinators solve, walk you through the code required to use them, then give a complete coding demo with a new Xcode project.
Coordinators were pioneered on iOS by Soroush Khanlou - he does tweet from time to time at @khanlou, but really you should check out his blog at khanlou.com.
I've also published articles on coordinators you might find useful:
- How to use the coordinator pattern in iOS apps: www.hackingwit...
- Using closures with the coordinator pattern: www.hackingwit...
- How to create a custom Xcode template for coordinators: www.hackingwit...
A brilliant video, such clarity!
Whenever you import UIKit, Foundation is no longer required as UIKit already imports Foundation! You'd know this but others would learn from you :)
I'm just starting out with iOS programming, and of course my first try is with XCode 11. Following video/text gives no compile runtime errors, but when you tap on on of the buttons, it enters the IBfunction but never executes the controller method. After some searching, it appears that you need to do something a bit different now. Instead of making code changes in AppDelegate, I had to put it in SceneDelegate on the scene function. my SceneDelagte now looks like this, and it is working! Thanks for a great tutorial and explanation!
import UIKit
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var coordinator: MainCoordinator!
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else {
return
}
let appWindow = UIWindow(frame: windowScene.coordinateSpace.bounds)
appWindow.windowScene = windowScene
let navController = UINavigationController()
coordinator = MainCoordinator(navigationController: navController)
coordinator.start()
appWindow.rootViewController = navController
appWindow.makeKeyAndVisible()
window = appWindow
}
.
.
.
Thanks man! It's working now.
Thanks
Thank you! This was very helpful..
Paul Hudson, you could consider adding the above in your website so that people with Xcode 11 can follow your tutorial better!
Man thx so much. I'm also just starting out and it didin't work following the tutorial. Your changes helped me now it's working.
God bless you
Thank you so much, the way Paul wrote it in the "Sundays" ebook did not work even if he updated the code for the SceneDelegate, your way is working perfect. Hope Paul will update the book including your code
I've been following your content on HWS for a long time now, You are the best Paul ! No competition REALLY , among the too many good iOS teachers I know you can easily stand out because of your true understanding of apple's frameworks and tools. You provide clear and COMPLETE content. I'm gonna tell all my friends about and tell them to subscribe ;)
That's very kind of you - thank you for the generous words!
Single storyboard file causes a lot of trouble when working in team. I would also create separate storyboard file for each view controller with the same name. It will help you with merge requests with less conflicts. Anyway, nice and clear tutorial 👌
If you are using Xcode 11 or newer and want to use the new SceneDelegate, you need to move the AppDelegate code to the SceneDelegate.scene method. After the guard clause, you create the navController, the coordinator, and set the rootViewController. I don't think you need the code to initialize the window because that's taken care of.
guard let _ = (scene as? UIWindowScene) else { return }
let navController = UINavigationController()
// Don't forget to add var coordinator: MainCoordinator? to the top of the class
coordinator = MainCoordinator(navigationController: navController)
coordinator?.start()
window?.rootViewController = navController
window?.makeKeyAndVisible()
thank you so much, couldn't figure out what was wrong with my code
@@tetracosanoic the same situation
Hi Paul! thank you so much for all the hard working you and your team are doing for us all those years!
I'm an iOS developer and came across you tutorial here, and just wanted to NOTE something:
When using Xcode 14.2, I managed to get this pattern design to work ONLY when implementing the code at 12:00min ('AppDelegate'->'DidFinishLaunching') at ('SceneDelegate'->'WillConnectTo' method)
Here's my version of it:
guard let windowScene = (scene as? UIWindowScene) else { return }
let navigationController = UINavigationController()
mainCoordinator = MainCoordinator(navigationController: navigationController)
mainCoordinator?.configRootVC()
window = UIWindow(windowScene: windowScene)
window?.rootViewController = navigationController
window?.makeKeyAndVisible()
So, to anyone having trouble implementing this tutorial at 2023 - ENJOY!
Thx again 😎
thank you so much it really helped!
Dear Paul, thanks so much, this is one of the best material (for more advanced programmers interested in architecture, design patterns) you ever made. I am so grateful. I tried once to implement the coordinator pattern, but struggled due to the mutual dependencies. You made it completely clear to me in this video. You saved my day! Thanks god for making you a SWIFT lecturer. 👍🏻
can you show more examples on where the child coordinators can be used, also an app where we have multiple coordinators?
Agree 100% ...
I'm on the case!
Christmas is coming early! Thanks Paul.. Could you please consider covering - dealing with default back button (ie replacing with custom), avoiding duplicate child coord entries (its an array so is possible - I had an app the if you pressed 2 buttons at once it would as 2 flow A and flow B... rare case but I did have to prevent this from happening. Wonder if childCoord could be a set? Looking forward to this release!!
Looks like he did just that:
czcams.com/video/ueByb0MBMQ4/video.html
@@twostraws Great tutorial overall, but it doesn't work entirely for Xcode 11.. Ken Papagno has commented below as to what changes are needed.. could you update those in your website under a new section called Xcode 11 maybe so that they don't face issues?
I like that you are narrating most of the things you do in the video.
Yes, that's certainly my intent.
Liking this 3 years after i watched it the first time
An awesome explainer on coordinators! Thanks Paul
Thank you!
This is the whole chapter of your Patterns book)) I use this pattern now. Very useful. (btw I have 8 of your books)
I don't actually include coordinators in Swift Design Patterns, because it's a bit too much of a UI pattern rather than a general language pattern. I did try, but I just don't think it fitted well. Thank you for your support!
@@twostraws I checked right now, and you're true. It might be some video then. But I'm pretty sure that I learned this pattern from your material. The code snippet I have is exactly the same. (So much stuff to learn, so I don't even remember where I've learned it from)))
@@stanymiles This is the video version of an article I wrote, which probably explains it: www.hackingwithswift.com/articles/71/how-to-use-the-coordinator-pattern-in-ios-apps
Nice explanation of the concept. I have one comment regarding components coupling. Yes, applying coordinator you decouple one VC from the other, but you introduce VC+Coordinator coupling instead. As one of possible solution to resolve it we may use blocks like onButtonPressed: () -> Void , which we can inject into VC and do implementation of the block right inside of coordinator. What do you think?
Found the answer in the end of next video...
Wonderfully done. This solves a lot of horrendous amount of task just to navigate from one scene to another. Absolutely Brilliant... I love this.
Thank you for this video! You just made it clear to me! Congrats!
I'm glad to hear it! Hopefully I'll add a follow-up video soon.
Helpful video. Thanks for making it. If you ever tire of Swift, you could work as an animator at Pixar. In that case, please make a WALL-E prequel. I would like to see how the apocalypse happened and how WALL-E experienced it.
Great Video Paul. Explained in easy way.
I am new to iOS and looking forward to your live app session.
Great video Paul! It would be great to have a video on how to implement/when to use Child Coordinators. Right now when I do Coordinators I only have one. An example showing how to make a destroy children, and also showing how to use Coordinators to create different views on iPhone vs iPad would be super helpful!
I'm working on it!
Thank You So Much, Paul. 🤝👏👏👌 It's pretty clear and precise. Finally I Understood this pattern.
A good overview for the Coordinator Design Pattern! Thumbs up!
Such a good explanation. How about navigating back or popping view controllers and handling view controllers that are not pushed but presented?
This is the 1st video of you I watched... AWESOME ! Thanks a lot
Great tutorial. Thanks for everything! Subscribed.
Is there anyway to download the demo project?
else
tip: you should start providing the links to demo projects 😄
Great tutorial!
I still think navigation is pretty easy the normal way. The main problem for me is deciding on a navigation stack or going modal. I think with a project greater than around 15,000 lines of code, I would look at a coordinator pattern. In other words if it's a simple app, I would go with the normal way of doing things.
awesome! It took me a while to get here but finally I got it.
@Pual Hudson Such a perfect guide as always! But, how DRY is a code? You need to write all the methods inside MainCoordinator and repeat the same code in every method. Is there a way to avoid it?
I am thinking: If all the 'Coordinator' is actually doing, is navigation. Why wouldn't we not use just an instance of UINavigationController as "Coordinator"? Even more, when every instance of UIViewController needs a 'var coordinator : Coordinator' while the property 'var navigationController' is already implemented by UIKit for free in every VC. The creation of new VC, as well as the navigation stuff would also be "outsourced' to the, well lets say, to the "MainCoordinatorNC" that inherits from UINavigationController. Bad idea?
Thank you a lot for this. It was a pleasure to watch.
I'm glad you enjoyed it! 👍
Thank god for saving me of using storyboards lol
Thank you for that!
Dear Paul. Can you recommend any links on how would the co-ordinator pattern be applicable to Swift UI?
MVC and Apples setups are pretty easy to understand and work well. Coordinators can easily become over engineered, and everyone creates an app that uses a different coordinator implementation, which you'll have to learn EVERY TIME! Yes, work in the industry long enough and you'll see that every app has a different Coordinator implementation that's just as different as you could imagine, without the supporting documentation (like you have with apples standards library).
You should put each storyboard in their own file so that other users on git can work on other classes while you work on your classes.
Great class about Coordinators! Thanks!
I'm glad you enjoyed it - make sure you watch the advanced coordinators video, which goes over lots of extras!
Confused as to why this isn't working for me, i've copied the code bit by bit and whenever i click on the buttons nothing seems to happen. Strange
This is awesome. So much to digest. Would be nice to be able to get the source code.
Awesome video. But how to handle a custom transition presentation of a view controller.
I came up with a solution inspired by functional programming. My view controllers manage presenting the next view controller on their own, but which view controller they present is decided by a closure. All they know about is what data they can provide to the next view controller and possibly some labels for the data. For example, if you have a view controller that allows you to select three numbers using sliders and submits these numbers with an ok button, that one would take a closure of type (Float, Float, Float) -> UIViewController and some description what those sliders mean etc
If you have some chain of views, this essentially amounts to currying. A view controller is just a higher order function.
Both solutions are mostly equivalent, but they have pros and cons. Coordinators introduce a clearer separation between logic and view, but they are more verbose and one has to remember assigning the coordinator to each view controller. My solution gives you a clear idea what data a view can provide and consume and you can see entire workflows ahhh a glance, but I actually tend to declare them in root view controllers of storyboards (which do nothing but coordinating those Workflows and maybe hold some references that they pass down). It’s a bit of a technical artifact that coordinators in my solution happen to be view controllers. Also, your solution makes it easy to instantiate the exact same view controller from everywhere by calling some function which is nice and avoids a boilerplate in my solution that I have to tell every view controller that wants x where to find x; on the other hand, that kind of looks like tight coupling through the backdoor as you replace knowing which view controller to instantiate by knowing which coordinator function to call.
Thanks for information. I just do not understand one thing, if we are using coordinators to break coupling than why do we couple each viewcontroller to exactly one coordinator? Does not this coupling breaks whole idea of coordinators, and making viewcontroller not reusable?
not really, if u have some view controllers like A -> B -> C -> D, but now if u need to add a "X" view between B and C, you would need to go on both to change the flow to A -> B -> X -> C -> D (very simple example) which on a large app might cause problems you won't see at first
But if you're using coordinators u just have A,B,C,D,X... and change what points to what in the coordinator....
remember in the example the view controller func buyTapped, the "coordinator?.buySubscription" can be anything. And even if you change the flow of the app, you won't need to change this file (ViewController or BuyViewController), only the coordinator, cause the viewController doesn't know what view coordinator?.buySubscription is...
Where do we use the childcoordinators array
I can recommend coordinators. Really nice video explaining it Paul, as always.
I am wondering though, why would the Coordinator protocol describe having a UINavigationController?
It should be up to the specific coordinators wether or not it presents things in a navigationcobtroller or something else like a tabbarcontroller or even something custom.
The viewcontrollers also don’t need to know that, but with the protocol, they do.
Am I missing something?
Yes, I think that's a fair comment - it's usually a better idea to expose as little as possible.
What a brilliant video and I followed the videos instructions there was only one thing that doesn't work when I initialize each ViewController's Coordinator as a weak var functions of the coordinator doesn't get called and when I just remove the weak keyword from coordinator's initialization everything works great idk why and how and this happens in Xcode 11.5
Nice and clear explanation! I am guessing the view controllers should hold a reference to the Coordinator protocol but not the concrete MainCoordinator class?
You are great! Can you show coordinator for those who don't use storyboards?
Amazing Clarity of Concept. Thanks Paul. 1 question here, navController in MainCoordinator is visible to all viewcontrollers ?
I don't get it why we need to set vc.coordinator = self. And what's the point of the childCoordinators?
Great video, what about coordinators with multiple Storyboard?
Could you please suggest me, how to build custom navigation bar with left and right bar items/buttons using coordinators? For eg. Once user does login and navigates to home screen, that is logout button as right navigation bar item. This will display in all the screens after login screen. How to handle such thing? That all logic goes somewhere in coordinator?
Would be nice to see a parallel with SiftUI
Is there any advantage in using storyboards for isolated view controllers over Xibs for each one? Great video as always, Paul!
Code only > SB > Xibs
Storyboards give a few bonus features that you don't get elsewhere, such as being able to design specific table view cells rather than using prototypes.
Amazing!
Thank you!
looks like I'm missing the childCoordinators variable being initialized. the project build fails since the initialization of the MainCoordinator is not full.
hi @paul, i want to know how coordinator works if we want to add subview to current view controller ? subview can be like child view controller or view with date picker...can be anything...
whose responsibility to add subview to parentview , will it be a coordinator's responsibility or that viewcontroller's responsibility ?
nice video to explain mvvmc
looks good Mr. Hudson
I'm glad it's useful!
Paul do you think it would be a good idea to mark the coordinator in VC's as Coordinator! instead of Coordinator? I mean, anyway without the coordinator the app will have no sense.
Super late response, but notice that coordinator has a weak reference, which is just a pointer to an object that doesn't protect the object from being deallocated by ARC. That means that you cannot know for sure whether the coordinator variable holds a reference to the AppCoordinator when you try to access it in the VC.
What if I want to present another NavigationController instead of push
great! big thx
Rama and shyam are playing football.is it simple or compound sentence,? Sr plz explain it
Would it be possible to use this practice on programmatically created views/viewcontrollers? Say a dynamic pop-up view.. how does the coordinater handle constraints on dynamic views? how and where should the constraints be handled?
Great video btw :-)
Great video.How to use this coordinator pattern for separate the delegates and datasources
They are different issues. If you watch my Swift on Sundays videos you'll see I talk about pulling out data sources quite a lot.
Paul Hudson oky
15:53 is it violating Interface segregation principle ?
Yes , but on 15:13 he explains why and recomend to use protocols in larger apps. (Sorry for my bad english haha )
Is anyone having an issue where the frame size of UIWindow is too small for any device? I've tried two different projects.
Thanks for sharing. Do you use Viper pattern ? It will be great to see implementation of app with Viper.
I'm glad it was useful! No, I don't use Viper much - MVC and MVVM mostly.
Dude you rock, Imma pay ya a coffee in person one day. 5:13 7:39
Paul, giving all view controllers "weak var coordinator: MainCoordinator?" dependency is not a good thing...
@@MsUzoAgu several ways. 1) pass a closure instead of coordinator for callback, 2) hide MainController behind a protocol.
Can view controller delegate prepareForSegue in coordinator?
Can it? Sure - it can just pass on the call. *Should* it? No; I don't recommend using segues.
Storyboards and XIBs are not needed.
We all shall use Code and Previews
Simple, Great.
Thank You!
Where can I download the tutorial project?
Super awesome!!! 😭
What? Ofcourse you can reuse Segways... and the point is that you want it hard-coded so you have a clear flow. Sounds like coordinators are just another adventure in over-engineering. I've been using Coordinators in this pattern and if you have some interesting design, you'll run into all the same problems as and more when it comes to displaying content. In fact, it becomes more complex because the state of screens becomes lost as you move between views. I would only recommend coordinators for large enterprise applications without novel/unique design.
I am trying to replicate this, but when I run it, I get a build fail "Type ViewController does not conform to protocol "Storyboarded" in ViewController.swift.
I thought this was effectively disabled through removing Main. I think I have followed all your steps... can you help? Thank you.
Check that method name in Storyboarded protocol is the same as method name in Storyboarded extension.
wow, this is awesome
I love this video. Thanks a lot. But can you show how to use coordinators with multiple storyboard?
How can you declare the coordinator in the ViewController as being 'weak'. When I do this, the compiler says'weak' must not be applied to non-class-bound 'Coordinator'; consider adding a protocol conformance that has a class bound. Did I miss something or is it because I'm using Swift 5 now.
hey, did you put to class keyword to your protocol. such as CoordinatorProtocol: class {...} ?
I have one doubt here:
UIViewController object have storyboard property initialized. Will it increase memory if we create new main storyboard instance everytime?
Where did you saw?
What is proper way move from Buy view controller to account view controller using coordinators?
Buy view controller must be removed from stack?
You tell the coordinator to pop it from the stack (or remove if using modal) - but you have to create a custom back button as the standard back button pops it for you. There are some gotta's with coordinators that are not discussed in this intro video.
What if we need to use this pattern in multiple storyboard like if I have 2 storyboard how can I use this pattern there ?🤔
same as you, maybe we should pass storyBoard name as an argument in function
I get a compile-time error: “Method ‘instantiate()’ in non-final class ‘ViewController’ must return ‘Self’ to conform to protocol ‘Storyboarded’. I checked, and the static func instantiate() does return Self, namely:
return storyboard.instantiateViewController(withIdentifier: id) as! Self
Is it because my Xcode only has Swift 4.0? The error points to the line: static func instantiate() -> Self {
I'm not able to debug small snippets of code, I'm afraid. I definitely suggest that you start by updating Xcode to rule out any problems, but once you've done that and compared your code to mine on screen, you're welcome to email me if you still have problems.
GOLD!
I'm glad you liked it!
This is so awesome and i can see the benefits...but for some reason i am still not getting it 100% :/
And I don't want to copy/paste the code and just use it without understanding how it is made from skratch. I guess i will put this video on repeat and try to figure out which pieces i am missing :)
Great video!
Feel free to tweet me (@twostraws) if you have specific questions. I hope to make a follow-up video addressing common questions, so please ask!
@@twostraws Tnx Paul! I will do that if i hit a wall again. You explained it very well so i expect going over the video and getting some reading done will clear things up :)
@@twostraws HA! I actually did it! It feels so much clearer now. I guess this is what you feel every day....kinda :) I feel i have the power to manipulate the world now!
I acutally went over your article and it helped going over code line by line with your added comments there. Awesome stuff!
Someone just told me I might be somewhere here... Thanks for the shoutout! And I hope this joke will have a long life! 😁
How would you use this with view controllers that need information to know what to display? E.g. a profile would need an ID. Just as parameters to the coordinator functions?
You inject the data into the VC when you init them within the coordinator.
@@mikapps9349 how does the coordinator get it though?
@@oboomabom when you create the coordinator you give it all the data in needs so it can pass what is required to VCFoo or VCBar etc...
@@mikapps9349 I'm a bit confused. If it needs the data when it's created then isn't that, in a way, a soft dependency with the destination view controller? Like how would I use a coordinator to go to the profile of one of my users that I'm displaying in a table view on my current vc?
Say you have a UsersCoordinator, which controls showing / hiding your views. One view (View Bar) could be a list of all students, another view (View Foo) could be a single profile of one user. Note view bar and foo don't know about each other at all! Flow example, you want to show View Bar.... the coordinator's role is to show this. You may have a UserModelController class (that's injected into the coordinator) which you can ask for allUsers or a singleUser. Coordinator shows View Bar, and provides the VC with all necessary info. You then select a cell to show a users profile. You ask the coordinator - showFooView(withUserID:) and pass in that users ID (for example) then within the coordinator you can ask UserModelController ... get this user with this ID and then inject that into the FooView. All flow is directed through the coordinator. This is how I do it, maybe there are other ways. With protocols etc, but this simple. Hope that helps. Otherwise create a question on stackoverflow.
How should we complete the part at min 12:00 in Xcode 11 since the introduction of the Scene Delegate file?
I tried to put the code as yours but it doesn't work. I tried to put that in Scene Delegate but it doesn't work.
Thanks
Take a look at this comment above from Łukasz Gierałtowski
"If you are using Xcode 11 or newer:
1) Coordinators project > General > Deployment Info > Target > set 12.1
2) AppDelegate.swift > remove both UISceneSession methods,
3) Info.plist > remove Application Scene Manifest property."
@@joemccraw2374 You can add the below code to your scenedelegate will connect to session class
window = UIWindow(frame:windowScene.coordinateSpace.bounds)
window?.windowScene = windowScene
let navController = UINavigationController()
mainCoordinator = MainCoordinator(navController: navController)
mainCoordinator?.start()
window?.rootViewController = navController
window?.makeKeyAndVisible()
Awesome.
Thank you!
Start here 3:33
Just wondering what if we also wanted to pass data between VCs. With Coordinator pattern, should coordinator code be made to take care of that too. How do we let VC know the coordinator telling "If you want me to push this to so and so VC, then you are also supposed to supply me with this and this data".
@David van Enckevort Thanks David, So Essentially that code also need to go into Coordinator, right?
@David van Enckevort Thanks, this helps.
do you have an instagram or other kind of social media page?
Great video, subscribed. Curious how wasn’t subscribed yet ¯\_(ツ)_/¯
Already used coordinators for one of small projects after reading your post “How to use the coordinator pattern in iOS apps”.
Now I’m thinking about slow migration to MVS+Cordinators for one of the projects with really huge VC classes. Could be interesting to hear opinion and expierience from others about risks and edge cases for slow migration. No way to rewrite everything in one release, there are more than 40 screens in that app.
You probably hadn't subscribed because I don't do that much on CZcams - I'm figuring things out slowly :) You don't need to dive in with coordinators everywhere; try adding it to just part of your app and go from there.
Nice but keep em under 10 mins.