What a difference a ViewModel makes.
Good architecture should make it easy to do bug fixes, add new features, and help speed up the development process, which in turn translates to less development costs, both in the short and long run.
For the reasons stated above, MVVM (or MVVM-C) works best for us, and we’ll explain how we use it in a little while. But first, let’s go over why we decided to move away from the very architecture Apple provides — MVC.
From the desk of our co-founder and iOS team leader, Marko.
The trouble with MVC
The pattern divides the software into three different parts, where Model represents data, View represents user interface and the Controller acts like the glue between the two. This seems straight-forward enough, but as the apps grow in complexity, this approach shows its shortcomings.
As per Apple’s instructions, everything in the MVC pattern should be classified as either Model, View or Controller, but this apporach leaves you asking: Can everything really be neatly sorted into one of the three, without seriously overloading one of the components?
All of the code and logic that’s hard to place ends up in the ViewControllers, both making them massive and littering them with too much responsibilities. In turn, they become non-reusable, difficult to maintain, more prone to bugs and harder to test.
Why go with MVVM?
Here Model contains all the business logic, like getting the data from the database or API, parsing the data and dealing with some custom logic. It’s in charge of business rules, data access, model classes etc.
View represents the entire user interface visible to the user and comprises both UIViewController and UIView.
ViewModel prepares all the information for the user interface. It communicates with the ViewController through a defined protocol and provides an easy way to test the entire ViewController.
In this approach, ViewController serves as a bridge between ViewModel and View and it doesn’t know anything about the business logic. Basically, it’s data-agnostic.
As result, ViewControllers are strictly defined, which makes debugging and reusing them much easier.
So, how does that actually work?
At DECODE we separate every screen into multiple UIViewControllers based on the Composition Pattern.
Let’s say a project we’re working on has a screen showing current user profile, like the one in the Facebook app, and that it has the following logical units:
- User profile header
- Photos section
- Friends section
- About section
This is how we would organize the hierarchy:
The main function of the UserProfileContainerVC is to lay out others ViewControllers, where each child ViewController is responsible for one specific feature, adhering to the Separation of concerns principle.
By adopting ViewController composition, we can break down one complex ViewController into many simple ViewControllers.
Every ViewController is in turn formed of the following elements, each of them provided in a separate file:
- VM (ViewModel)
- VMProtocol (ViewModelProtocol)
The Model in MVVM comprises the Business Layer and API layer. It’s a pretty important and complex topic that can hardly be summed up and explained in a couple of gists, so we’ll dedicate one of the future posts to it.
In the mean time, let’s just go over it quickly. The Business Layer here is in control of Data Model and other services that do some specific logic which can be extracted and encapsulated.
The API layer implements the protocols defined by the Business Layer, and is also in the charge of all HTTP calls to the server. It also has the ownership of handling the access token. It implements the logic for getting the token from the server, storing it and using it with every HTTP request.
This is a UIView subclass and its task is to define a layout for a specific ViewController.
Initialization of all buttons, labels and constraints comes inside of this class and its task is to provide all UI components to the ViewController through the exposed getters.
If you need to change anything regarding design or layout later on, this is the only place you have to look. The ViewController loads a custom view described in the paragraph below. The basic idea here is to separate the initialization of UI elements and layout from handling various events. In this approach, ViewController is just an intersection between UI elements and the data prepared by its ViewModel. It does not, in any way, need to know anything about the Business Layer and API layer.
To make data binding easier between view controller and its view model, we often use ReactiveCocoa/RxSwift. We’ll also go into more detail on using the Xcode InterfaceBuilder (Xib files) with this architecture in the following weeks.
The role of the ViewModel is to prepare the data for presentation, making sure there is no tight coupling between the Model and the View.
When we implement this architecture, we use VMProtocol, because it makes it easier for us to administer any necessary changes without additional fuss and to mock all data needed for acertain ViewController before we have the real deal to implement.
You can think about VMProtocol as a contract between UIViewController and the BusinessLayer.
An excellent rule of thumb is to check if some of your code implemented in VM requires UIKit. If it does, this is a big warning.
Exceptions for this rule are UIImage and CGFloat.
Implementation of ViewModel often calls classes from the Business Layer, it communicates with application data model etc.
Now, onto the MVVM-C
Stay tuned for the next post, when we talk more about MVVM-C, C being the Coordnator.