This is the second post in a series of posts about iOS project architecture. Check out my previous post “What is VIPER?” if you are interested in learning more about different iOS architectures.
In this post, I will introduce the MVVM architectural pattern and how it can be used in an iOS project. I will start out with a short introduction of the core concepts of MVVM. After that, I will go through its different components in more detail. Once I have outlined the architecture’s different components and their functionalities, I will present a more detailed description of the relationship between the different components in MVVM.
MVVM - A short introduction
MVVM stands for three architectural components; the Model, the View and the ViewModel. The ViewModel is a key component in MVVM which differentiates it from the commonly used MVC pattern. The use of a ViewModel provides a clear separation between an application’s business logic and its application logic (its UI layout related logic).
In MVC the Controller can have a direct relationship with a Model, which means that the Controller is responsible for both the layout related logic, the data retrieval and model manipulation logic, as well as the application’s business logic that needs to be applied to the Model for the specific screen. The ViewModel in MVVM changes this relationship between the Controller and the Model.
A core principle of MVVM is that the View and the Model are not aware of one another. Instead, the ViewModel works as a mediator between the View and the Model to supply the View with the data it needs, correctly formatted for a specific use case, in accordance with the app’s business logic. The diagram below gives us a visual representation of the components’ relationship, which will be described in more details throughout this article.
How MVVM’s components interact with one another - A simple example
Let’s say that we want to display a list of blog posts in an app, these blog posts have a publish date. According to our business logic, we should display the number of days that have passed since the blog post was published in a text label. In the app, we have a Model, which contains variables about a blog post including the Date variable representing the blog post’s publish date. This Model is accessed by a ViewModel which contains our business logic. The ViewModel contains a function that compares the stored publish date with today’s date and calculates how many days have passed since the post was published, from this calculation, the ViewModel creates a String which represents the number of days that have passed since the post was published - for example, “2 days” - this String is being returned by the function. To display the blog post we have a View which uses the timestamp value returned by the function in our ViewModel to display the number of days passed since the post was published inside of a text label. This example expresses the core relationship between the components in MVVM.
A few benefits of MVVM
Using the MVVM pattern in an application provides us with several benefits. By separating our app’s business logic and the application logic we enforce the single responsibility principle, where the app’s different classes are responsible for a specific, single behavior. The responsibility of the ViewModel in our example above is to apply logic which determines which String the function should return. While the View described above is responsible for determining which label should display our timestamp string.
By following the single responsibility principle, we also add testability to our application, since we can test a specific functionality related to our business logic, independently from UIKit and from our app’s view layout related functionality.
MVVM in iOS
When talking about MVVM in an iOS project the commonly used UIView and UIViewController are usually coupled within MVVM’s View component and is simply referred to as the View. In order to provide a more concrete overview of how the different components in MVVM look like in an iOS project, I have decided to split the View section below up into a UIView and a UIViewController section.
Like in MVC, a Model is a representation of the application’s data. An app may have multiple Models. A Model may simply be a Struct or a Class which contains variables that represent values for a specific data object, a Model may also be a Core Data entity.
As mentioned above, the View component’s responsibility in an iOS project is split between the UIView and the UIViewController. Its joint responsibility is to provide the application with a visual element, that a user can interact with.
The UIView is the component that is visible to a user, which the user can interact with directly. It provides the user with an interface to obtain information from and to enter information to our application.
The UIViewController (or simple ViewController) goes hand in hand with the UIView. The ViewController is simply, as the name suggests, the controller of the UIView. Its job is to handle user input events and to determine how the UIView should behave to provide user feedback. The ViewController contains all the UIKit specific logic and all the logic related to which of the UIView’s different components should display what. The ViewController is responsible for the UIView’s Delegate and Datasource methods. For a UITableView for example, the associated ViewController’s responsibilities include things like determining the section and row count for the UIView, determining what should be displayed in the tableView’s different cell elements and the UITableView’s navigation bar buttons’ behaviors.
Okay, so this is where MVVM differentiate from MVC, with the added ViewModel component. The ViewModel contains the presentation and the business logic required for our application. It’s the ViewModel’s responsibility to contain the Models that are required for a specific screen in the app, as well as handling formatting of these Model’s data in accordance with the app’s business logic. The ViewModel works as a mediator between the app’s Model and the app’s View component.
A key differentiator between a ViewModel and a Controller is that the ViewModel is independent of any UI logic. This means that we should not have to import UIKit to our ViewModel (except for in rare edge cases, for example; when the ViewModel requires access to UIColors which depend on UIKit).
What differentiates the ViewModel from a Model is that the ViewModel contain functions for data manipulation, formatting of data, presentation logic application, and the business logic that we need to apply before displaying data.
Back to our previous example
In the example earlier in this post we wanted to display a blog post and a label with the time that passed since a post’s publish date. In the example, I simply describe the functionality of the View as a single component of MVVM.
I would like to expand on this example in more detail. If we separate the example’s View into a UIView and a UIViewController the UIViewController’s responsibility is to utilize the value representing the timestamp provided by our ViewModel. It’s the UIViewController’s responsibility to determine which UI element that should display the value provided by the ViewModel. The UIView, on the other hand, is simply the visual component that contains different labels which are directly visible to the user. Since the UIView might contain multiple labels it’s the UIViewController’s responsibility to decide which of these labels that should display the value retrieved from the ViewModel and assign the value to the UIView’s specific text label.
Where do the network calls go?
In MVVM there is not an independent component that is specifically dedicated to network requests and data parsing. For the sake of code reusability and to follow the single responsibility principle, we can separate the network related functionality out to one or several helper classes. These helper classes will be responsible for the direct network calls.
However, we will still need to determine which of the components in our MVVM application should interact with these helper classes. Our ViewModel is responsible for containing our app’s business logic, in order to enforce testability and single responsibility we should try to keep this business logic as independent as possible. We want the ViewModel to have a minimum amount of dependencies, and we want it to be responsible for accessing and handling data models that we already have retrieved. To fulfill these preferences, we will interact with our network helpers from our ViewController.
The different components’ relationship
Now when we know what the different components of MVVM are responsible for, let’s go through what the relationship between these components looks like.
An app often contains different screens which provide the user with different functionalities, the screens have different responsibilities with different looks and feels. The same thing applies to the app project’s MVVM components. A project usually contains multiple Models, Views, and ViewModels which have their own functionalities and responsibilities. We combine a specific UIView with a specific UIViewController to display information to a user in a specific View. The formatted information that is being displayed in the View is retrieved from a specific ViewModel and the data that the ViewModel is formatting is often sourced from one or several specific Models.
The graph above demonstrates the relationship between the different components in MVVM.
In MVVM, the View contains a reference to a ViewModel and has access to the variables and functionalities that this ViewModel provides. The ViewModel however, is not aware of the View and does not communicate directly to the View. This means that the View and the ViewModel’s relationship is unidirectional.
Our ViewModel in MVVM has a direct relationship with a Model and can access the values held by the Model.
There is no direct relationship between the View and the Model components in MVVM. A View that requires data from a Model, contains a ViewModel which it can interact with to retain the Model’s data in the format that the specific View requires it in. This is where the ViewModel works as a mediator between the View and the Model.