MVVM and Coordinator pattern together
Using RxSwift and RxCocoa to bind the view.
Today lets talk about a common problem when creating an application. What design pattern should a choose?. Actually there are many pattern you can use when building an iOS application: MVC, MVP, MVVM, VIPER etc. These are some of most used in iOS, each one with different approaches over each other but finally which one is the best?. It depends what are you working on, what big is your project and how much time you can spend building the app.
Now lets talk about two different patterns:
- MVVM: the view model is responsible for exposing (converting) the data objects from the model in such a way that objects are easily managed and presented. in other words this is a bridge between the model and view/viewController. Is a elegant way to avoid Massive view controllers moving the logic to the View Model.
- Coordinator: the coordinator or mediator will be in charge of our application routing (navigation) then the views/viewControllers doesn’t need to know about where they are or if they are inside of a navigation, they only need to send the navigation actions to the coordinator/mediator.
MVVM in action
first lets see how MVVM works and why this is very important for us.
This is a little bit easy to understand how it works:
- The view/viewController send an event or tasks to the viewModel
- The viewModel perform a task (fetch something in the backend, perform an operation, gets the model, etc.)
- The viewModel notifies to viewController through: KVO, Delegates, Callbacks, Binding etc.
In fact, nowadays, if you hear “MVVM” — you think Reactive programming, and vice versa. Although it is possible to build the MVVM with the simple bindings, RxSwift and RxCocoa will allow you to get most of the MVVM.
This way is how we are gonna make our example using RxSwift to fetch some data and RxCocoa to bind the data to the view.
- First we have the viewController:
If you know RxSwift this is pretty easy if not I’m gonna explain how it works:
- we have viewModel property, this is the reference to our view model, actually this a protocol so we don’t hardcode a specific type and the viewModel can be anything that implements the protocol.
- we have searchBarText that is an Observable to the searchBar text.
- we perform the search with our viewModel calling the method .fetchRepositories(for: searchBarText) that returns an observer and then we can subscribe to it and wait for the task result.
- finally we have the method .drive(tableView.rx.items(cellIdentifier: String(describing: RepositoryCell.self), cellType: RepositoryCell.self)). This is very similar to bindTo method but using a driver, if you don’t know what a driver is go here.
- Second part the viewModel:
This is easier than before :)
- Here we have only one method that receives an Observable of type string, guess what, that Observable will be the searchBar text what was declared before in the viewController and that’s it :) the view Model is ready.
- Third part the model:
Actually I have separated the model in two parts:
- The networking: here we have the networking call and when we handle the response, handling errors and parsing it.
- The Repository Model: here we have the object representation of a Repository and that’s it a model shouldn’t contain any specific logic or networking calls it only contains data and anymore.
It looks really cool our app is able of bring data from an API call and show it in a table view with very little code over more we have cool architecture and defined roles.
And here we have the benefits:
- Distribution — Now our viewController doesn’t take care about the models anymore, it just send events to the view model and it perform the task, when it’s finished sends the response back to the view controller, actually the view controller doesn’t know what really happens under the hood because now it isn’t its responsibility :).
- Testability — the View Model knows nothing about the View, this allows us to test it easily. The View might be also tested, but since it is UIKit dependent you might want to skip it.
- Reusability — As our viewControllers doesn’t perform an specific task it’s easy to reuse a lot of code and views in the project as well as the view models.
- Scalability — now the project is easy to change or update because the roles are well defined and the view controllers doesn’t perform a lot of task as before with MVC (Massive view controllers).
It’s time to Coordinators
our application looks very good but we still have a big problem, what if I want to another screen, who is charge of this task, the viewController, the viewModel?. The answer is none of them, we need a new element in our architecture to handle the app routing.
Lets try to add a coordinator to our app but first we need to understand where and how it should go.
Ok it looks very similar than before but now we have a new component the coordinator, It is charge of handle our application routing. usually you have many coordinators in your project because you have different navigations and modules in your app. You can relate the coordinators with a container like UITabBarController, UISplitViewController etc, or with a navigation such as UINavigationController these are most used containers and navigations in your app. Every time you need to create one of those you know that need a coordinator to handle the flow :).
It’s time to write some code and watch a coordinator in action.
- First lets change our view controller to know when the user selects a row.
This is very simple we only call the method modelSelected(Repository.self) and then we bind the repository to the view model.
- Now update the view model.
here we have some new stuff lets see:
- now our view model conforms a protocol called Transitionable that only have a property called navigationCoordinator.
- subscribe to repositorySubject well be called each time user select a row in the table view.
- call performTransition this doesn’t need explanation :)
- Final part the Coordinator.
This is quite easy as well, we have a custom init() method where we create the view model and navigation controller then we assign navigationCoordinator to self.
Finally our coordinator implements the method performTransition(transition: Transition) and that’s it we have all app components ready and working.
This has been all here hope you would like this architecture and enjoy playing with this 👋 here is the final project.
We have seen what powerful is use design patterns together and how it can help you have a strong architecture and scalable application.
Also we have seen how to use RxSwift to bind to view and the viewModel this is the best approach (for me) if you are gonna use MVVM everything is clearer and you can save more than 40% of code with RxSwift anyways there are a lot ways to do this. up to you which one choose 🤘.