MVC stands for Model-View-Controller, and it’s a widespread architectural pattern for software development. It’s the de facto design pattern for Cocoa development, and it has been for many, many years. Most of us can’t imagine building applications without it. Both UIKit (iOS) and AppKit (macOS) make frequent use of MVC. It almost seems as if we don’t have another option to build applications for iOS, tvOS, macOS, and watchOS.
Even if you’re unfamiliar with the Model-View-Controller pattern, if you have the ambition to develop applications for one of Apple’s platforms, you need to learn how views (iOS) and windows (macOS) relate to controllers and which role the model plays in a typical Cocoa application. Fortunately, MVC is easy to learn.
In this short series, I explain what MVC is, what it looks like in a typical Cocoa application, and why it may not be the best solution for Cocoa developers.
1. An Example
Let me show you what the MVC pattern looks like in a typical Cocoa application. The example I’ll be showing you focuses on iOS, but everything we discuss also applies to tvOS, macOS, and watchOS. Open Xcode and create a new iOS project based on the Single View Application template.
Name the project MVC, and set Language to Swift and Devices to iPhone. I’m using Xcode 8 for this tutorial. The project’s configuration options may look a bit different if you’re using Xcode 9.
As the name implies, the Model-View-Controller pattern defines three components, the model, the view, and the controller. Let me show you where you can find these components in a typical iOS project.
Controllers
The controllers of an iOS application are view controllers, instances of the UIViewController
class or a subclass thereof. The UIViewController
class is defined in the UIKit framework. Because we chose the Single View Application template when we set up the project, Xcode created a controller for us to start with, the ViewController
class, defined in ViewController.Swift. It inherits from the UIViewController
class.
As the name implies, a UIViewController
instance is responsible for controlling a view, an instance of the UIView
class. Every view controller in an iOS project keeps a strong reference to a view, another component of the Model-View-Controller pattern. The UIView
class is also defined in the UIKit framework.
Views
We can find the view component in the main storyboard of the project. Open Main.storyboard in the Project Navigator on the left and inspect the View Controller Scene. The scene contains a view controller, an instance of the ViewController
class, and it manages a UIView
instance.
Select View in the storyboard on the left and open the Identity Inspector on the right. The Class field of the view is set to UIView
. In an iOS application, views are typically instances of UIKit’s UIView
class or a subclass thereof.
Models
Thus far we’ve explored the controller layer and the view layer. But where can we find the model layer of the project? The model is almost always specific to the project you’re working on, and it’s up to you to define, implement, and use the model of the project. I write model, but you usually have multiple models, depending on the complexity of your project.
Let’s add the final piece of the MVC puzzle by creating a model. Create a new Swift file and name it Person.swift.
Select Person.swift in the Project Navigator on the left and define a structure named Person
. We define three properties:
-
firstName
of typeString
-
lastName
of typeString
-
age
of typeInt
struct Person { let firstName: String let lastName: String let age: Int }
You now have a model you can use in your project. Let’s keep it simple and define a property, person
, of type Person?
in the ViewController
class. We create a Person
instance in the view controller’s viewDidLoad()
method and assign it to the person
property.
import UIKit class ViewController: UIViewController { // MARK: - Properties var person: Person? // MARK: - View Life Cycle override func viewDidLoad() { super.viewDidLoad() // Create Person person = Person(firstName: "John", lastName: "Doe", age: 40) } }
What we see in this example is very common in Cocoa applications powered by the Model-View-Controller pattern. The view controller owns and manages the model, and it uses the model to populate its view. In a more complex application, you load the model’s data from a persistent store or fetch it from a remote back end.
Let’s define an outlet for a UILabel
instance in the view controller and, in the main storyboard, add a label to the View Controller Scene.
import UIKit class ViewController: UIViewController { // MARK: - Properties @IBOutlet var label: UILabel! ... }
In the view controller’s viewDidLoad()
method, we safely unwrap the value stored in the person
property and use its data to set the text
property of the UILabel
instance.
override func viewDidLoad() { super.viewDidLoad() // Create Person person = Person(firstName: "John", lastName: "Doe", age: 40) // Populate Label if let person = person { label.text = "(person.lastName), (person.firstName) ((person.age))" } }
The result isn’t very surprising if you’re familiar with Cocoa development. This is what we end up with.
2. What Is Model-View-Controller?
The Model-View-Controller pattern is easy to understand and pick up. Despite its simplicity, you can find a wide range of flavors of the MVC pattern. MVC only offers a basic blueprint that can be modified to the platform it is used on.
The Model-View-Controller pattern you’re familiar with on iOS, tvOS, macOS, and watchOS differs in subtle ways from the original definition. While the differences compared with the original definition are subtle, they have a significant impact on the code you write as well as on the maintainability of the project.
Smalltalk
The Model-View-Controller pattern is an old design pattern. It made its first appearance in the 1970s in Smalltalk. The pattern was conceived by Trygve Reenskaug. Over the years, the Model-View-Controller pattern made its way into many languages and frameworks, including Java, Rails, and Django.
I mentioned earlier that the MVC pattern breaks applications up into three distinct components: model, view, and controller. The original implementation of the pattern defines that the view is responsible for displaying the model’s data to the user. The user interacts with the application through the view layer. The controller is in charge of handling user interaction and manipulating the model’s data as a result. The view visualizes these changes to the user. As illustrated in the below diagram, the model plays a key role in the MVC pattern as it was designed by Reenskaug.
MVC and Cocoa
The implementation we use in Cocoa development differs from Reenskaug’s original design. Take a look at the below diagram to better understand what these differences entail.
As I mentioned earlier, the view and the controller share a close relationship. In a typical iOS application, a controller holds a strong reference to the view it manages. The view is a dumb object that knows how to display data and respond to user interaction. The result is a highly reusable component.
The controller plays a vital role in Cocoa applications powered by the Model-View-Controller pattern. It takes over some of the tasks of the model in Reenskaug’s original MVC implementation. The view and the model don’t communicate directly with one another. Instead, the model is usually owned by the controller, which it uses to configure and populate the view it manages.
I hope you can see the subtle differences between Reenskaug’s original implementation in Smalltalk and the Cocoa implementation we have become accustomed to. The differences are minor, but, as I’ll discuss in a moment, the impact they have is important.
3. The Good: Separation of Concerns and Reusability
Before we take a look at the problems MVC introduces, I’d like to show you why the Model-View-Controller pattern has become such a popular and widespread pattern in software development. The Model-View-Controller pattern we use in Cocoa development has a number of clear benefits it inherited from Reenskaug’s original implementation.
The most obvious advantage of the Model-View-Controller pattern is a separation of concerns. The view layer, for example, is responsible for presenting data to the user. The model and controller layers are not concerned with data presentation. But if you’ve been using MVC in a Cocoa project, then you know that this isn’t always true. I’ll talk more about that in a moment.
A direct benefit of this separation of concerns is reusability. Each of the components of the Model-View-Controller pattern is focused on a specific task, which means that the building blocks of an MVC application are often easy to reuse. It also allows for these components to be loosely coupled, increasing their reusability. This isn’t true for every component, though. In a Cocoa project, for example, controllers are often specific to the application and not good candidates for reuse.
The views and models of a project, however, are highly reusable if designed correctly. Table and collection views, for example, are UIView
subclasses that are used in millions of applications. Because a table view delegates user interaction to another object and asks a data source for the data it needs to display, it can focus exclusively on data presentation and user interaction.
4. The Bad: Massive View Controllers
Most developers quickly grasp what the Model-View-Controller pattern brings to the table and how it should be implemented. Unfortunately, the Model-View-Controller pattern also has an ugly side. I already wrote about reusability and separation of concerns. I’m sure I don’t need to convince you of these benefits. A table view is highly reusable and incredibly performant. Developers can use standard UIKit components in their applications without any need for subclassing or customization.
Hitting the Limits of MVC
But that’s only part of the story. You know when you’re starting to hit the limits of MVC when massive view controllers have sneaked into your project. It’s time for change when you’re ploughing through hundreds or thousands of lines of code to find that one method you’re looking for.
Dumping It in the Controller
Most developers know what goes into the view and model layers of a typical Cocoa application powered by the Model-View-Controller pattern. But which component is responsible for formatting the data that’s displayed to the user? Remember that views are supposed to be dumb and reusable. The view shouldn’t need to format data. Right? It should only know how to present data and respond to user interaction. Should the model be concerned with data formatting?
And what about networking? That’s certainly not the task of the view. Should it be delegated to the model? That doesn’t sound right. Why don’t we slip that piece of code into the controller. It doesn’t feel right, but it’ll do for now.
After many lines of code, you end up with a controller that is ready to burst and a nightmare to test. Testing? I hear you. I wouldn’t want to test a view controller suffering from massive view controller syndrome either.
5. A Better Solution
You started with good intentions, but you ended up with a project that has a collection of overweight controllers that are difficult to manage and maintain. You’re not looking forward to adding new features to the project you’re working on because opening up those view controllers makes you sick in your stomach. Does this sound familiar?
It’s important to realize that this is a common scenario. Many developers hit the limits of the Model-View-Controller pattern and realize they need something better. Chances are that you’ve already been looking at several alternatives, such as MVP (Model-View-Presenter) or MVVM (Model-View-ViewModel).
In the next installment of this series, I’ll zoom in on the Model-View-ViewModel pattern. It will feel strangely familiar if you’ve already worked with the Model-View-Controller pattern. But the Model-View-ViewModel pattern brings a few improvements to the table that work very well for Cocoa development.
And while you’re waiting, check out some of our other posts on Cocoa app development!
-
SwiftWhat’s New in Swift 4
-
iOS SDKRealm Mobile Database for iOS
-
iOS SDKFaster Logins With Password AutoFill in iOS 11
Powered by WPeMatico