What is Model-View-Controller?

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?

MVC schematic blankA 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?

MVC schematic modelThere 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?

MVC schematic viewAll 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 but2016-07-19_06-56-52ton 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?

MVC schematicThe 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.

2016-07-19_08-21-53

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

2016-07-19_08-27-27

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.

2016-07-20_05-34-04

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 center alignment button  button in the attributes inspector. Your layout should look something like this:

2016-07-21_08-44-05

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:

2016-07-21_08-43-38

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.

2016-07-25_06-49-35

Select a pizza, and you get  a price.

2016-07-25_06-49-59

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
        }
    }
    
    
}



10 thoughts on “What is Model-View-Controller?”

  1. Why do you use the view in the viewcontroller? You should use an UIView controlled by an UIViewController, then you can separate View from Controller.
    What about using more than one view in a viewcontroller? Your approach would not be the best.

    1. I’m not sure what you are referring to. In this case the view is on the storyboard.

      In my experience, there should only be one view. Multiple views are subviews of a superview.

  2. Another great post, Steven. What has always confused me about MVC in an iOS/Xcode context is the separation of the view from the controller (actually, it seems more like a lack of separation). After all, we have these things called UIViewControllers that are full of UIViews. Is the visual representation of a scene on the storyboard considered the ‘view’ in MVC? Is it the collection of UIViews (and subClasses) that appear on a VC scene of a storyboard? Considering the fact that you don’t have to use a storyboard to create views, where/what is the view then?

    I hope that makes sense. Thanks!

    1. Yes, controllers and views are a little smushed together, but they are different. Yes what you see on the storyboard is the view.

      Probably the best way to start is to back up slightly. The class UIViewController has a property view. This< is how and where the controller does anything to the view. The view itself will have properties and methods to communicate with the controller. Thus in the controller view is pointed to by the property view.

      The question really is what’s in that view. For non-gaming environments, there are two possibilities, or a mix of them. You can add them programmatically with view.add(subview) or you can do them on the storyboard. if you add them in code, then you specify a series of Subclasses of UIView and set the properties and methods to match. On the other hand, you can put thing in the storyboard, which is stored as an XML file. The compiler will put together those views for you, but its the same subclasses of UIView. Since we can’t add properties and methods on the storyboard, we use outlets and actions to set them up in the view controller as outlets and actions.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s