Mastering MVVM on iOS
There are a plenty of posts on the internet about app architectures in the iOS development world. Today I will show some tips for using MVVM architecture while developing iOS apps. I am not going to show other architectures if you need them there is a great post (https://medium.com/ios-os-x-development/ios-architecture-patterns-ecba4c38de52 ).
The main problem of Apple MVC is mixed responsibility, which leads to the appearance of some kinds of problems such as Massive-View-Controller.
We should accept that UIViewController is the main component in Apple’s iOS SDK and all the actions are started and built across this entity. Despite the name, it is more View than a Classic Controller (or Presenter) from MVC( or MVP), because of callbacks like viewDidLoad, viewWillLayoutSubviews, and other view related methods. That is the reason why we should ignore the Controller keyword in the name and use it as View, where the role of real Controller takes the ViewModel.
ViewModel is the full data representation of the view. Every View should hold only one ViewModel instance. Generally, ViewModel uses a manager to fetch data and transforms it into necessary format. Let’s dive into examples:
Here we have ViewModel that fetches items via DataManager and holds it inside some variable. It also holds an error and refreshing state, which brings opportunity to build UI with all required conditions.
As you can see above we have ItemsViewController that represents a list of items in UITableView. It holds ViewModel instance and asks it to fetch data in the viewDidLoad callback. We are also passing closure, which reloads UITableView as soon as the data is fetched. UITableViewDataSource methods also use the ViewModel to get the count of cells.
Bindings between View and ViewModel is the main idea of MVVM architecture, where developers can work on ViewModel code and designers can work on View in Interface Designer. In the first example, we used closures, because there is no binding ability in iOS SDK out of the box. In real life applications, you can use some of popular FRP extensions, like ReactiveCocoa, RxSwift or Bond. I prefer Bond library for its simplicity.
This is the same ItemsViewModel, but now we use reactive programming to observe changes.
Let’s look at bindViewModel method, here we are binding ViewModel to View. Whenever refreshing value changes it sets activityIndicator’s isAnimating property. Or when items are changed, UITableView reloads. As you can see, reactive bindings simplify code in most cases.
Sometimes we have complex Views with multiple data sources. For example, Instagram user profile where we have some information about the user and all photos related to this user. Good job here is separating this logic into two or more ViewModels. But we have a rule: every View should have only one ViewModel. In this case, the best option is using ViewModel composition. Let’s look at examples:
As you can see, we have UserProfileViewModel which holds two another ViewModels and collects data from them. We also have the refreshing state which combines two refreshing states of internal ViewModels. The second important point on line 36, where ViewModel formats data into the required form. View just has to bind components to ViewModel and show data as is.
ViewModel is really nice and simple way of separating presentation logic into another entity, which helps us to avoid Massive-View-Controller and keep things easier to control and covered with Unit Tests. That is where we go, simple and testable architecture.
Feel free to ask any question in comments below.