Model-View-ViewModel in Swift
Model-View-Controller, or MVC for short, is a widely used design pattern for architecting software applications. Cocoa applications are centered around MVC and many of Apple's frameworks are impregnated by the pattern.
A few months ago, I was working on the next major release of Samsara, a meditation application I have been developing for almost ten years. The settings view is an important component of the application.
From the perspective of the user, the settings view is nothing more than a collection of controls, labels, and buttons. Under the hood, however, lives a fat view controller. This view controller is responsible for managing the content of the table view and the data that is fed to the table view.
Table views are flexible and cleverly designed. A table view asks its data source for the data it needs to present and it delegates user interaction to its delegate. That makes them highly reusable. Unfortunately, the more the table view gains in complexity, the more unwieldy the data source becomes.
Table views are a fine example of the Model-View-Controller pattern in action. The model layer hands the data source—mostly a view controller—the data the view layer—the table view—needs to display. But table views also illustrate how the Model-View-Controller pattern can fall short. Before we take a closer look at the problem, I would like to take a brief look at the Model-View-Controller pattern. What is it? And what makes it so popular?
What Is the Model-View-Controller Pattern?
The MVC pattern breaks an application up into three components or layers, model, view, and controller.
The model layer is responsible for the business logic of the application. It manages the application state. This also includes reading and writing data, persisting application state, and it may even include tasks related to data management, such as networking and data validation.
The view layer has two important tasks, presenting data to the user and handling user interaction.
A core principle of the MVC pattern is the view layer's ignorance with respect to the model layer. Views are dumb objects. They only know how to present data to the user. They don't know or understand what they are presenting.
The view layer and the model layer are glued together by one or more controllers. In iOS and tvOS applications, for example, that glue is a view controller, an instance of the
UIViewController class or a subclass thereof.
A controller knows about the view layer as well as the model layer. This often results in tight coupling, making controllers the least reusable components of an MVC application. The view layer and the model layer don't know about the controller. The controller owns the views and the models it interacts with.
Separation of Concerns
The advantage of the MVC pattern is a clear separation of concerns. Each layer of the MVC pattern is responsible for a clearly defined aspect of the application. In most applications, there is no confusion about what belongs in the view layer and what belongs in the model layer.
What goes into controllers is often less clear. The result is that controllers are frequently used for everything that doesn't clearly belong in the view layer or the model layer.
While controllers are often not reusable, views and models are easy to reuse. If the MVC pattern is correctly implemented, the view layer and the model layer should be composed of reusable components.
If you have spent any amount of time reading books or tutorials about iOS, tvOS, or macOS development, then you have probably come across people complaining about the MVC pattern. Why is that? What is wrong with MVC?
A clear separation of concerns is great. It makes your life as a developer easier. Projects are easier to architect and structure. But that is only part of the story. A lot of the code you write doesn't belong in the view layer or the model layer. No problem. Dump it in the controller. Problem solved. Right? Not really.
Data formatting is a common task. Imagine that you are developing an invoicing application. Each invoice has a creation date. Depending on the locale of the user, the date of an invoice needs to be formatted differently.
The creation date of an invoice is stored in the model layer and the view displays the formatted date. That is obvious. But who is responsible for formatting the date? The model? Maybe. The view? Remember that the view shouldn't need to understand what it is presenting to the user. But why should the model be responsible for a task that is related to the user interface?
Wait a minute. What about our good old controller? Sure. Dump it in the controller. After thousands of lines of code, you end up with a bunch of overweight controllers, ready to burst and impossible to test. Isn't MVC the best thing ever?
How to Get Rid of Fat Controllers?
In recent years, another pattern has been gaining traction in the Cocoa community. It is commonly referred to as the Model-View- ViewModel pattern, MVVM for short. The origins of the MVVM pattern lead back to Microsoft and it continues to be used in modern Windows development.
The MVVM pattern introduces a fourth component, the view model. The view model is responsible for managing the model and funneling the model's data to the view via the controller. This is what that looks like.
Despite its name, the MVVM pattern includes four major components, model, view, view model, and controller.
The implementation of a view model is usually straightforward. All it does is translate data from the model to values the view(s) can display. The controller is no longer responsible for this ungrateful task.
Because view models have a close relationship with the models they consume, they are considered more model than view.
How to Implement the Model-View-ViewModel Pattern?
I have to warn you, though. The Model-View-ViewModel pattern will change the way you write and think about software development. But that is a good thing. The following tutorials take a closer look at MVVM in practice.
In these tutorials, I use Samsara as an example to apply the MVVM pattern. The goal is to refactor the settings view of Samsara using MVVM. With the help of MVVM, I end up with a skinny view controller and several view models.
Along the way, you learn more about the MVVM pattern and how it applies to Cocoa development. You also learn how Swift can help us elegantly apply the MVVM pattern thanks to protocols, protocol extensions, and enumerations.
If you want to take a shortcut, then I recommend taking a look at Mastering MVVM With Swift. In this series, I take you on a deep dive into the Model-View-ViewModel pattern. You learn how to implement MVVM in a Swift project and I also show you how to use MVVM with RxSwift and Apple's Combine framework.