Mastering MVVM With Swift
Is MVC Dead
Model-View-Controller, or MVC for short, is a widely used design pattern for architecting software applications. Cocoa applications are centered around the Model-View-Controller pattern and many of Apple's frameworks make heavy use of the Model-View-Controller pattern.
Last year, I was working on the next major release of Samsara, a meditation application I've been developing for the past few years. The settings view is an important aspect 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 responsible for managing the content of the table view and the data that's 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 incredibly 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, and very often does, fall short. Before we take a closer look at the problem, I'd like to take a brief look at the Model-View-Controller pattern. What is it, what makes it so popular, and, more importantly, what are its drawbacks?
What Is It
The MVC pattern breaks an application up into three components or layers:
- 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're presenting. This makes them flexible and easy to reuse.
The view layer and the model layer are glued together by one or more controllers. In an iOS application, that glue is a view controller, an instance of the
UIViewController class or a subclass thereof. In a macOS application, that glue is a window controller, an instance of the
NSWindowController 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 application based on the Model-View-Controller pattern. The view and model layers 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 Model-View-Controller pattern is responsible for a clearly defined aspect of the application. In most applications, there's 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, view and model objects are often easy to reuse. If the Model-View-Controller pattern is correctly implemented, the view layer and the model layer should be composed of reusable components.
If you've spent any amount of time reading books or tutorials about iOS or macOS development, then you've probably come across people complaining about the Model-View-Controller pattern. Why is that? What's wrong with the Model-View-Controller pattern?
A clear separation of concerns is great. It makes your life as a developer easier. Projects are easier to architect and structure. But that's 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're 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's obvious. But who's responsible for formatting the date? The model? Maybe. The view? Remember that the view shouldn't need to understand what it's presenting to the user. But why should the model be responsible for a task 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 Can We Solve This?
In recent years, another pattern has been gaining traction in the Cocoa community. It's commonly referred to as the Model-View-ViewModel pattern, MVVM for short. The origins of the MVVM pattern lead back to Microsoft's .NET framework and it continues to be used in modern Windows development.
How does the Model-View-ViewModel pattern solve the problem we described earlier? The Model-View-ViewModel 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 components or layers:
- View Model
- and Controller
The implementation of a view model is often straightforward. All it does is translate data from the model to values the view layer 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're often considered more model than view.
In the next episode, we take a closer look at the internals of the Model-View-ViewModel pattern.