Back to blog

Understanding Model-View-Controller (MVC) on iOS


Aasif Khan
By Aasif Khan | Last Updated on March 26th, 2023 3:52 pm | 5-min read

Model-View-Controller (MVC) is an exceptionally powerful software architectural pattern for creating iOS apps. MVC is the answer to the question: “How should I organize code in my iOS app?”

You already know Object-Oriented Programming (OOP). OOP organizes your Swift code in classes that have properties and functions, much like bob is an instance of class Human, that has properties legs and noseSize.

Model-View-Controller builds on top of Object-Oriented Programming. It structures the flow of data and interaction in your app. When you’re asking yourself: “How should I pass data from one part of my app to another?” then Model-View-Controller is a smart answer.

When it comes to architectural patterns and apps, you can choose from a great number of designs: Model-View-ViewModel (MVVM), Reactive Programming (RP) and Model-View-Whatever (MVW). SwiftUI, the newcomer, also affects how we use MVC. And of course, you can also use a number of smaller software design patterns, like delegation and extension.

In this application development tutorial, you’ll learn how to use Model-View-Controller (MVC) to better structure your app and its data. Many of the Apple frameworks use MVC, so it pays dividends to understand this architectural pattern and apply it to your iOS development. We’ll also discuss MVC vs. SwiftUI, Apple’s new UI framework.

What’s Model-View-Controller (MVC)?

Let’s get started with messaging. No, not sending chat messages – but sending messages from one part of your app to the other.

A “message” in iOS development is simply the name of a method, and any parameters associated with it, that you send to an instance of an object.

Like this:

dog.sit()

You’re sending the sit() message to dog. Why don’t you just call those “functions” and “objects”? Well, a function and an object are static – they don’t have intent. You want to be clear about what calling a function implies, i.e. the transmission of information.

We need to make that distinction going forward. Model-View-Controller isn’t just a structure for a bunch of functions and classes — it’s about sending a message!

If you think about messages, and sending and receiving them, you’re automatically thinking about sending and receiving data. In iOS development, messages are often much more complex than dog.sit(). Check out this example:

rook.move(steps: 10, direction: .forward)
rook.move(steps: 3, direction: .right)

In the above example, you’re sending messages to the chess piece rook. You tell it to move 10 steps forward, and 3 steps to the right. With messaging you’re instructing it to move, sending two pieces of data to the chess piece.

Enough about messaging. Let’s get into Model-View-Controller…

MVC, short for Model-View-Controller, is an software architectural pattern. You use it to create the architecture for your app, kind of how an architect designs a building before a builder constructs it.

The Model-View-Controller concept describes 3 components:

  • Model, a wrapper of data
  • View, a representation of a user interface (UI)
  • Controller, an intermediary between the Model and the View

You use these 3 components together to structure your app. Every component has a distinct role:

  • The Model encapsulates a particular set of data, and contains logic to manipulate that data. When you think about accounting software, an Invoice is a model. When you think of a Twitter app, a Tweet is a model.
  • The View is an object that the user can see, in a user interface (UI). In the accounting software, a UILabel that displays the invoice address is a view. In a Twitter app, the TweetView is a view that displays a tweet.
  • The Controller controls all logic that goes between the View and the Model. It transports messages between the View and the Model, and vice versa.

In iOS development, and the UIKit framework in particular, you’ve probably already worked with a ton of Models, Views and Controllers without knowing it.

Some examples:

  • Objects like UIButton, UIView and UILabel are all examples of Views. More complex views like MKMapView contain many visual properties like mapType and isZoomEnabled.
  • Objects like UIViewController, CLLocationManager and UINavigationController are examples of Controllers. In iOS you also use delegates, a special kind of controller that you can hand-off messages to.
  • When using the Core Data SDK, your .xcdatamodeld file contains your app’s Models. In Realm, these models are created as simple classes. In Firebase and Parse Servers these models are represented as JSON objects.

The final piece of the Model-View-Controller puzzle is the flow of data. You can see that in the image above.

Between the 3 Model-View-Controller components, you can see how the messages can flow from one component to the other.

This happens in 4 ways:

  • The View informs the Controller when a user interaction takes place
  • The Controller updates the View when the data changes
  • The Controller updates the Model when the data changes
  • The Model notifies the Controller when data changes

You can see 2 kinds of flow in the above diagram:

  • The top flow from View → Controller → Model happens when a user interaction in the View causes the data in the Model to change
  • The bottom flow from Model → Controller → View happens when the data in the Model changes and the View needs to update accordingly
  • Let’s talk about a quick scenario for Model-View-Controller: a to-do app.

Imagine that the to-do app has a number of Views in a list, one for every task. Every View is backed by a Model that has the data for that task, like title and isCompleted.

Now, the following happens:

  • The user taps on a task View, and this causes the iPhone on-screen keyboard to pop up – the task title can now be edited
  • The user edits the task title, like changing it to “Get groceries”, and taps the Submit button
  • The Controller now responds to the data change and updates the Model accordingly

Likewise, this can happen too:

  • The Model data for a task changes, because of data that comes in from a cloud back-end like Firebase
  • The Model then updates the Controller, sending a message that its data has changed
  • The Controller will then propagate that message and update the View accordingly

That’s it! That’s all there is to it. Let’s now figure out how to put this theory into practice…

Why You Should Use MVC

Model-View-Controller is a fundamental concept to understand, especially in iOS development. Many iOS frameworks, like UIKit, use the MVC pattern to structure data flow and messaging.

One of your jobs as an app developer is to keep your apps maintainable. This means that you should document your code, keep your code concise and readable, and adhere to a supporting structure.

Model-View-Controller is that structure. It keeps your code from becoming one big pile of chaos. That’s the first and foremost benefit of using Model-View-Controller. You get more deliberate at designing the flow of data. Don’t write spaghetti code!

The second reason is that most developers know and understand MVC. When you need to pass your code on to another developer, or when you work together, it helps to have clear communication. Using the standard MVC is one way to make sure this transition and collaboration goes smoothly.

A third reason to use MVC – one that’s often overlooked – is that it gives you confidence as a developer. You can’t lose sleep over worrying whether your code is structured OK if you use Model-View-Controller, or… at least you worry less than you would have if you hadn’t used any structure!

Model-View-Controller also increases the modularity of your code, and aids code reuse. According to the Don’t Repeat Yourself (DRY) principle, you want to avoid duplicating similar lines of code as much as possible.

Object-Oriented Programming already helps with that. Instead of creating a bunch of variables to organize tweets, for instance, you can create a Tweet class and assign it title and user properties.

In your app you often represent data in visually similar ways, even if the data is different. It doesn’t matter if you’re showing a tweets timeline, a profile’s tweets or trending tweets – the tweets and their UI stays the same.

Combining OOP and MVC means that you’d create a TweetViewController that can display any assortment of tweets, instead of creating a separate FavoriteTweetViewController and ProfileTweetController. Less repeating, more code reuse!

Even if you won’t end up using Model-View-Controller as it’s intended, learning about MVC is a great introduction into app architecture, and helps you design your apps more deliberately.

Practical Uses for MVC in iOS Development

The Model-View-Controller architecture on iOS has one caveat: it’s often abstracted away!

Let’s say you’re building a chat app, much like the chat app from this iOS coding tutorial. The chat app uses Firebase, a popular cloud-based back-end platform.

The chat app uses a ChatViewController, a Controller component. In that controller you find the following code:

let query = Constants.refs.databaseChats.queryLimited(toLast: 10)

_ = query.observe(.childAdded, with: { [weak self] snapshot in

// Append the chat message to the `messages` property …
// … then update the messages view controller
})

So far so good! This is clearly a Model observation or “binding”. You’re attaching the view controller to a particular Firebase query, so that when the data on the back-end changes, you get updates in the view controller.

But then… inside the observe callback, i.e. what happens when new messages come in, you add the new message to the messages property of the view controller. The view controller uses a table view (from UIKit) to display cells on screen – one cell per chat message. Are those Views?

You can imagine it’s easy to lose track of Model-View-Controller in practical app development. Everything looks like a view controller. Why is the ChatViewController doing everything!?

Is the ChatViewController class a Model, a View or a Controller? After all, it displays data, it manipulates it, and it also stores it with the messages property…

This is where theory meets practice. It’s good to know that in practical iOS development, the roles of Model, View and Controller sometimes overlap. Nevertheless, you should be able to distinguish these roles in your app.

A few examples from the chat app:

  • The Firebase back-end, with its data structures, clearly has the Model role
  • The ChatViewController, a collection view controller subclass, with its logic and commands, clearly has the Controller role
  • The chat message views, that are collection view cells, clearly have the View role

Oftentimes, it’s the Controller that has the most overlap with other roles. It’s not an accident that in iOS development it’s called “View Controller”, i.e. a Controller that also is (partly) a View.

Likewise, many of the parts of the app are Model-View-Controller components themselves. A UIButton is a View, and the messages property contains instances of class JSQMessage, which is a Model.

This fuzzy role of the Controller is often called Model-View-Whatever (MVW), simply to indicate that practical development doesn’t have a black-and-white Controller component. The code is the controller.

That said, it’s not a carte blanche to put everything in the view controller. When you suspect your view controller is getting to heavy, try if you can move some of its responsibilities to the Views or the Models. You can also use other design patterns and abstractions, like delegation, a ViewModel or a helper.

Model-View-Controller vs. SwiftUI

Let’s do a quick encore, and discuss the role of SwiftUI versus Model-View-Controller. SwiftUI is a declarative UI framework for Apple platforms, released in 2019. It’s slated to become the defacto UI framework for iOS, essentially replacing UIKit, Storyboards, and view controllers.

Now that SwiftUI is here, should you still bother with MVC?

Yes, and for 3 reasons:

  1. SwiftUI hasn’t yet completely replaced UIKit, so for the time being, we’re still working with UIKit-based apps, and they still use Model-View-Controller
  2. MVC has historical and foundational relevance; when you learn the principles of MVC, it’s easier to master higher-level concepts like SwiftUI
  3. SwiftUI replaces only part of MVC, data flow with @Binding for example, but parts of your code will still contain controller logic, like delegates, helper functions, and services

A good approximation for the UIKit vs. SwiftUI adoption curve, is what happened to Objective-C when Swift was gradually adopted. SwiftUI is in an early adopter phase right now, same as Swift back in 2014.

Fast-forward a few years, and Swift is the default for new apps (and new developers). But is every iOS app built with Swift? Plenty of existing Objective-C apps, libraries and frameworks are still around. It’s worth it to understand Objective-C code, to the extent you need that in your situation.

Back to Model-View-Controller, because not all that’s UIKit is MVC. What’s SwiftUI’s role in MVC?

When we look at Model-View-Controller as we’ve discussed it here, one of the roles of the Controller is to facilitate the flow of data between the model. Strictly speaking, a View should cause data changes in a Model through notifying the Controller.

An often-heard argument against MVC is that the (View) Controller gets too heavy. The Controller is either a useless pass-through between the Model and the View, or it’s a massive view controller that does pretty much everything in your app.

SwiftUI takes a page from Model-View-ViewModel’s book, with both bindings and decoupling.

  • With bindings, roughly speaking, you create a connection between a View that’s on-screen, like a label, and a place where the label’s text goes, such as a Model. The SwiftUI framework abstracts away some of the responsibilities of the Controller, such as data flow.
  • The ViewModels then takes the place of the Model, adding in View-specific business logic — Controller stuff — so the Model itself remains loosely coupled from the View. This also reduces the role of the Controller.

See how we’re moving away from Model-View-Controller, but the concepts and approaches we’re using somehow still look a bit like MVC?

That’s why principles and fundamental topics never go out of style. You’re going to build apps with SwiftUI – and so will I – but at some point, you’re going to wonder what its origin story is.

Further Reading

What should you take away from this?

There’s one thing that never changes, the whole reason you use Model-View-Controller: the messages. Data still flows from one side of the app to another side, and it still does that in just two ways:

  • Data from the Model that updates the View (Model → View)
  • User interaction from the View that updates the Model (View → Model)

Both data flows go through the Controller, which in turn decide what should happen with the data. Regardless of what architectural patterns you end up using, you’ll still build something that looks like data and like a UI, and whatever goes in between. Neat, right?


Aasif Khan

Head of SEO at Appy Pie

App Builder

Most Popular Posts