If you are a beginning developer you’ll hear the term Model View Controller often, but may not understand what it is. You’ll see the UIViewController
class in an app, but my not completely understand what is happening here. In this lesson, I’ll describe this system and develop an app to demonstrate MVC from the Xcode 8/Swift 3.0 edition of my book Swift Swift View Controllers coming in October, though available now for Xcode 7/Swift 2.1.
What is a Class?
A class is a collection of data, which we call properties, and actions we can do to those properties, which we call methods.
Properties and methods are either public or private. A public method is one that a class other than the defining class can see and can use. Private means that the property or method is only usable and visible within the defining class. Other classes cannot see or use it. In Swift the private keyword makes properties and methods private. Swift’s calculated properties feature is another way to make properties private. In Swift, the default state makes a method or class public to the current target, but not other targets.
We tend not to like other people messing with our insides, and that is a good programming practice too. It is best to leave what is public to a necessary minimum. Keeping properties and methods private and not exposing all of our class is known as encapsulation.
Encapsulation allows us to make code as modular as plastic building blocks. Just as a few stubs come out of an otherwise standard sized brick, only a few usable methods come out of a class. Then they can attach to a lot of other bricks.
What is MVC?
A term heard often when working with Xcode, is MVC. MVC stands for Model View Controller. It is not an exclusive iOS or OS X term. It is a pattern of programming, a good organization of any program or application in a graphics-rich environment, and arguably any environment that interacts with the user. MVC separates two major parts of an application, the data and the user interaction, then adds an intermediary between them. Why is this important? You might write and publish an application for an iPhone, then decide an iPad version would be a good idea, then decide to make a OS X or web version. With MVC, you only change one part completely, the view and possibly some of the controller. The code handling your data never changes between the versions saving a lot of time and effort.
What Are Models?
There are parts of our program that deal with information we want to process. A pizza ordering system has a list of data giving us information about each person’s order. There may be other links to that data with more data about each customer, and about each pizza. In a pizza ordering system this is our model: the collection of all the data we will use in our ordering system. It does not in any way interact with the user. It does not display anything nor does it ask for input. It is just data. Here is a simple model:
class Pizza{ var pizzaDiameter:Double var pizzaTopping:String init(topping:String, diameter:String){ pizzaDiameter = diameter pizzaTopping = topping } }
This model is two variables, pizzaDiameter
and pizzaTopping
. I can describe a 10 inch pepperoni pizza this way by Pizza(topping:"Pepperoni", diameter:10.0)
. This is by code only – there is no user input. More often, the model contains methods to manipulate the data we have. For example, the above model could store crust information for the pizza and compute the radius and area from the diameter. We’ll initialize the method by giving the topping, crust, and diameter.
class Pizza{ var pizzaTopping:String var pizzaCrust:String var pizzaDiameter:Double var pizzaRadius:Double { get{ return pizzaDiameter / 2.0 } } let pi = 3.1415926 let crust = 0.1 init( topping:String, crust:String,size:Double) { pizzaTopping = topping pizzaCrust = crust pizzaDiameter = size } func howMuchCheese(height:Double) -> Double{ //Volume of pizza minus crust let c = pizzaRadius * pi * (1.0 - crust ) return c * height } }
This model is data for a basic pizza ordering system. There is still no user input or output here. Models might make calculations as we do in the howMuchCheese(height:)
method, but again there is no user interaction here.
What are Views?
All the user interaction happens is in the view. In Xcode, most people use Interface Builder to build their views. A developer can also programmatically create a view class to hold the different controls.
As the model never interacts with the user, the view never interacts directly with the model. The view doesn’t do much but sit there. It might respond to a user touch with feedback such as a color change when a button gets tapped or a scrolling motion at times, but that is all it does. The view does contain a lot of properties and methods and that tell us the state of the view. We can change the appearance and
behavior of the view through methods and properties. The view can tell the controller that there was a change in the view, such as a button getting pressed, or a character typed. The view can’t do anything about it, but it can broadcast something that the controller observes and reacts to.
What are Controllers?
The heart of MVC connects these two. Called the controller or view controller, it coordinates what happens in the model and what happens in the view. If a user presses a button on the view, the controller responds to that event. If that response means sending messages to the model, the view controller does that. If the response requires getting information from the model, the controller does that too. In Xcode,
@IBOutlet
and @IBAction
connect interface builder files with views to the view controller. For example, part of the view controller for the pizza app might be this:
class ViewController:UIViewController { //Instance of the model let pizza = Pizza( topping: "Cheese", crust: "Pan", size: 10.0) // a connection to change the view @IBOutlet weak var pizzaLabel: UILabel! //A connection to react to user action @IBAction func toppingSelectionButton( _ sender: UIButton) { pizza.pizzaTopping = (sender.titleLabel?.text)! displayPizza() } //A function to change the label func displayPizza(){ pizzaLabel.text = "\(pizza.pizzaDiameter)\"" + pizza.pizzaTopping } override func viewDidLoad() { super.viewDidLoad() displayPizza() }
Methods like displayPizza()
access the model’s property pizzaTopping
and pizzaDiameter
, then send them on to the view. Methods like toppingSelectionButton
react to user response on the view, sending information to the model.
Keeping Data Private
The key to MVC is communication, or rather the lack of it. MVC takes encapsulation very seriously. The view and the model never directly talk to each other. The controller can send messages to the view and the model. The controller never directly changes anything in either the view or the model.
As an aside, we need to talk about how Swift coddles us. For true encapsulation, our properties should all be private and we should write two special methods: a getter to retrieve a value and a setter to change a value for every property. For example the property pizzaDiameter
might be written this way:
var pizzaDiameter:Double = 0.0 func setPizzaDiameter(size:Double){ pizzaDiameter = size } func pizzaDiameter()->Double{ return pizzaDiameter }
Swift generates the setters and getters and type behind the scenes for you. You only need this
var pizzaSize = 0.0 var pizzaDiameter = 0.0
Then use it as a variable:
let z = pizzaDiameter / 2.0 //getter for pizzacrust let a = 2.0 //height of crust let v = pi * z * z * a //volume pizzaSize = v //setter
This creates the illusion you are directly modifying a property. You don’t need the setter or getter. You will get an error if you try to write the code with setters and getters.
Why I mention this is the illusion of directly modifying properties in the view and model by the controller. It isn’t directly modifying anything. Behind the curtain are getter and setter methods. The model can not assign values in the controller. Then how do we assign values from the model to the controller? The model can return a value in a getter called by the controller.
So to summarize, a view, a model and a controller cannot directly change a property in each other. A view and a model may not talk to each other at all. A view can tell the controller there is a change in the model. A controller can send messages in the form of method calls to the view and the model and get the responses back through that method. Now let’s look at a functional example in the next chapter.
Set Up the View
In Xcode, Create a new project as a Single-View Application with Swift as the language and a Universal Device. Call it SwiftMVCPizzaDemo.

On the bottom left of the storyboard, you will find this:

Make sure the button reads View as:iPhone 6s. If it does not , click where it says View as. You will get a toolbar of device frames appear underneath. Hovering over them, you will see a cursor indicating the device. Select the iPhone 6s, then click View as to dismiss the toolbar.

In the storyboard add a label, two buttons, and a segmented control. Make one of the buttons with a Red(#FF0000) background and White(#FFFFFF) lettering, make the title of the button read Clear.
Stretch the segment across the device so both blue margins highlight. Make the segmented control four segments with Cheese, Sausage, Pepperoni,and Veggie.
Stretch the label across the device between the margins like the segmented control. Set the alignment to center by clicking the  button in the attributes inspector. Your layout should look something like this:
Connect the View Controller
Open the assistant editor, and the ViewController.swift file should appear. Control drag the label to the code and release. Make an outlet displayLabel
.

Control-drag from the unlabeled button to the code. Make a action named sizePizza. Remember to change from an outlet to an action since Interface Builder picks an outlet by default. Set the Type to UIButton.

Now do the same for the red button, making an action clearDisplay. Control drag from the segment to the code and make another action toppingSegment you now have the following outlets and actions
@IBOutlet weak var displayLabel: UILabel! @IBAction func sizePizza(_ sender: UIButton) { } @IBAction func toppingSegment(_ sender: UISegmentedControl) { } @IBAction func clearDisplay(_ sender: UIButton) { }
A Multiple Selection Button
We will use preset buttons based on the size button to choose the size of a pizza. Go back into interface builder and click once on the blue button. Copy the button, then paste it four times for five buttons. Change the titles and arrange the buttons to look like this:
This is a way to have multiple buttons use the same IBAction
. The properties can be different for the buttons, in our case the titles. Each button reacts exactly the same to being pressed since they have the same IBAction
method. Our model will take the title string and convert it into a pizza size.
Make the Basic Model and Properties
Open a new file by pressing Command-N. Make a new Cocoa Touch Subclass of NSObject
called Pizza with Swift as the language. Add this code to the file that appears
class Pizza:NSObject { let pi = 3.1415926 var diameter = 0.0 var topping = "Cheese" var unitPrice = [ "Cheese": 0.03 , "Sausage": 0.06 , "Pepperoni": 0.05 , "Veggie": 0.04 ] }
With that small amount of code, we created a class with three properties and two constants. While there is a built-in constant M_PI
, we’ll use our own value for pi in area and volume calculations. The class sets a default value for the pizza topping to Cheese. The unitPrice
is a dictionary with pricing information.
Create Computed Properties
Besides declared properties like above, we’ll use some computed properties. Add the following code just under the pizzaPricePerInSq
dictionary declaration.
//MARK: Computed Properties var radius : Double { //1 -- computed property //must define a getter for a computed property get{ return diameter/2.0 } }
This declares the property radius
based on the values of other properties. Computed properties must declare their type, and must include a getter. In the radius
property, we compute the radius we need for area calculations by dividing the already declared property diameter by 2.0, then return the value.
Add another computed property:
var area : Double{ get{ return pi * radius * radius } }
The area
property returns a double with the area of our pizza using radius
and pi
. Add one more computed property to compute price for a whole pizza:
var price:Double{ get{ guard let price = unitPrice[topping] else{ return 0.0 } return price * area } }
Dictionary values are optional values, which means they could be nil
or a value. We use the guard
to unwrap the price
of a pizza. if that pizza topping does not exist, the code returns 0. Otherwise the code returns the area multiplied by the unit price. I’m aware this is a very contrived way of figuring out a price of a pizza, but it works for our purposes.
Conversion Methods in Models
The view has a bunch of buttons with titles. The view controller will ask the view for the strings of those titles. Sizes in our model are type Double
, not String
. Models may have conversion methods to take one type and convert it to the type it needs. Add this method under price
//MARK: - Instance Methods func diameter(from string:String)-> Double{ switch string { case "Personal": return 8.0 case "Small": return 10.0 case "Medium": return 16.0 case "Large": return 18.0 case "Jumbo": return 24.0 default: return 0.0 } } }
The code uses a switch statement to parse the strings to numbers, returning the value as a Double
.
Use the Model in the Controller
Open the ViewController.swift file. Controllers supervise both the model and the view. This app has a model and a view, but no real controller yet. View controllers need an instance of the model. Just under the view controller’s class declaration add the following:
let pizza = Pizza() //instantiate a Pizza object
This is all we need to instantiate a Pizza
object: declare the object as a constant and give a class for the object. Even though pizza is a constant, its properties are not forced to be constants. Using let
is better memory allocation than using var
.
In this app, pressing the clearDisplay
button will send a message from the view to the controller to run code to reset the application. That reset is a change of the view. Change the action clearDisplay
to this:
@IBAction func clearDisplayButton(sender : UIButton) { displayLabel.text = iLovePizza }
There’s a constant iLovePizza
in this action. At the top of the class add a constant
let iLovePizza = "I Love Pizza!"
Another common MVC interaction is to take information from the model and present it on the view. In our app, we’ll do all that in the label. Add this display method for the label:
//MARK: - Instance Methods func displayPizza(){ let displayString = String( format:"%6.1f inch %@ $ %6.2f", pizza.diameter, pizza.topping, pizza.price )//2 displayLabel.text = displayString }
The method makes a string displayString
from properties of pizza
, then sends it to the label.
Often the user changes the model by an interaction with a view. Add the following code for the sizePizza
button:
@IBAction func sizePizza(_ sender: UIButton) { let title = (sender.titleLabel?.text)! pizza.diameter = pizza.diameter(from: title) displayPizza() }
The size
button reads the title of the button called sender
, and assigns the correct diameter for a pizza to pizza
., using the conversion method diameter(from:)
In another example of changing the model by an interaction of the view, change the topping
property through the segmented control. Add this to the code for the topping
action
@IBAction func toppingSegment(_ sender: UISegmentedControl) { let index = sender.selectedSegmentIndex let title = sender.titleForSegment(at: index)! pizza.topping = title displayPizza() }
The view will react to a change in segment by sending a change message to the controller. When this happens, the controller gets the title
for the segment from the view then sets the model’s topping
property to that title
.
View controllers will need to do initialization to the model and view. often we do this in the viewDidLoad
method. Find the viewDidLoad
method in your ViewController
class. Add the following code to it:
override func viewDidLoad() { super.viewDidLoad() displayLabel.text = iLovePizza }
This sets up the view with an initial value.
Set the simulator to an iPhone 6s. Build and run the application.

Select a pizza, and you get a price.
The Whole Code
ViewController.swift
// // ViewController.swift // SwiftMVCPizzaDemo // // Created by Steven Lipton on 7/19/16. // Copyright © 2016 Steven Lipton. All rights reserved. // import UIKit class ViewController: UIViewController { // Mark: Properties and Outlets let pizza = Pizza() let iLovePizza = "I Love Pizza!" @IBOutlet weak var displayLabel: UILabel! @IBAction func sizePizza(_ sender: UIButton) { let title = (sender.titleLabel?.text)! pizza.diameter = pizza.diameter(from: title) displayPizza() } @IBAction func toppingSegment(_ sender: UISegmentedControl) { let index = sender.selectedSegmentIndex let title = sender.titleForSegment(at: index)! pizza.topping = title displayPizza() } @IBAction func clearDisplay(_ sender: UIButton) { displayLabel.text = iLovePizza } //MARK: - Instance Methods func displayPizza(){ let displayString = String( format:"%6.1f inch %@ $ %6.2f", pizza.diameter, pizza.topping, pizza.price )//2 displayLabel.text = displayString } override func viewDidLoad() { super.viewDidLoad() displayLabel.text = iLovePizza } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } }
Pizza.Swift
// // Pizza.swift // SwiftMVCPizzaDemo // // Created by Steven Lipton on 7/20/16. // Revision for Swift 3.0 // Copyright © 2016 Steven Lipton. All rights reserved. // import UIKit class Pizza:NSObject { let pi = 3.1415926 var diameter = 0.0 var topping = "Cheese" var unitPrice = [ "Cheese": 0.03 , "Sausage": 0.06 , "Pepperoni": 0.05 , "Veggie": 0.04 ] //MARK: - Computed Properties var radius : Double { //-- computed property get{ return diameter/2.0 } } var area : Double{ get{ return pi * radius * radius } } var price:Double{ get{ guard let price = unitPrice[topping] else{ return 0.0 } return price * area } } //MARK: - Instance Methods func diameter(from string:String)-> Double{ switch string { case "Personal": return 8.0 case "Small": return 10.0 case "Medium": return 16.0 case "Large": return 18.0 case "Jumbo": return 24.0 default: return 0.0 } } } 
Leave a Reply