The Swift Swift Tutorial: Adding an MVC Model Class in Swift

[Note: Updated for Swift 2.0/iOS9 9/1/15 SJL]

The Pizza Computer Mark II
The Pizza Computer Mark II

Last time we built a small application to compute the area of a pizza. Since it was missing a model, we are going to make an improved version of the application and add a model class, plus make a few UI changes. Since there are so many changes, we’ll start from the beginning. If you did not read the last post in the series, you will be fine though you might want to catch up on the concepts discussed earlier here. I’ll assume you have at least Xcode 7, and have some clue how to set up a story board and view controller.

Set Up the Storyboard

In Xcode, Create a new single-view Swift project. Call it PizzaDemo2. Go into the storyboard. On the bottom  center of the storyboard, you will find the class sizes button labeled w:Any h:Any. Click the button and change the class size to Compact Width Any Height:

2015-08-30_10-33-39

To the storyboard add a label, two buttons, and a segmented control. Make one of the buttons with a red background and white lettering, make the title of the button read Clear. Set the second button to a white foreground lettering and a blue background, but don’t set a title yet. Make the Segmented control four segments with Cheese, Sausage, Pepperoni,and Veggie. Your layout should look something like this

2015-09-01_06-49-45

I did not use constraints here, but if you want to you certainly can.

Set Up the View Controller

Open the assistant editor, and control drag the controls to make the following IBOutlets and IBActions. note in the comments which one is the red and which one is the blue button. Remember from last time to physically change from an outlet to an action for the buttons and the segmented controls. Interface builder picks an outlet by default.

    @IBOutlet var resultsDisplayLabel : UILabel!
    @IBAction func pizzaType(sender : UISegmentedControl) {
    }
    @IBAction func sizeButton(sender : UIButton) { //blue button
    }
    @IBAction func clearDisplayButton(sender : UIButton) { //red button
    }

Finish Setting up the View

In the last post in this series, we used a slider to specify pizza size. We will use preset buttons this time. We are going to use our blue sizeButton to specify the size in its label. Go back into interface builder and click once on the blue button. Copy the button, then paste it 6 times. Change the titles and arrange the buttons to look like this:

2015-09-01_06-17-37

In case you have never done this before, 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, but they react exactly the same to being pressed. Our model will take the title string and convert it into a pizza size.

Make the Basic Model and Properties

Between the import and the class ViewController: code, make a little space and add the following:

class Pizza
{
    let pi = 3.1415926
    let maxPizza = 24.0

    var pizzaDiameter = 0.0
    var pizzaType = "Cheese"
}

With that small amount of code, we created a class with two properties and two constants. That’s it. Swift does require initialized values, thus we have to  specify an initialization value. Implicit typing figures out what type to use by our initialized values. When we declare a variable in a class in Swift, it becomes a property of that class. Similarly declaring a constant becomes a constant of the class.

Add a Method..or is it a Function?

Adding methods is also easy. We did it last time in the view controller. A function within a class becomes a method of that class.

A Computational Method

Add the following under var pizzaType = "Cheese"

func pizzaArea() -> Double{
        return radius * radius * pi
    }

We have not had a case yet where we return a value until now. The pizzaArea() method returns a double. In Objective-C, and many C syntax languages we have the return value first, something like -(Double)pizzaArea{} In Swift, the return value is on the end with an operator -> to specify the type of the return value. if there is no return value, it is the equivalent of (void) in C-syntax languages. In this case we return the area.

We are getting a syntax error now since radius is not defined. We’ll come back to radius shortly and show one of the really cool features of Swift.

Swift’s Switch Switch

Swift made a few changes to the switch control Add this method under pizzaArea():

func diameterFromString(aString:String) -> Double {
    switch aString {
    case "Personal":
        return 8.0
    case "10\"":
        return 10.0
    case "12\"":
        return 12.0
    case "16\"","15\"":
        return 16.0
    case "18\"":
        return 18.0
    case "24\"":
        return 24.0
    default:
        return 0.0
    }
}

Switch in Swift does not use breaks. We can also list several cases for each case, so a 15″ and a 16″ pizza returns 16. The Swift switch does need all cases be covered, so the default: case is mandatory to catch everything not stipulated above the default. In our example, this will send 0.0 when we press the None button since it is not a specified size.

This method also has a parameter. We specify the variable name and then the type, separated by a colon for parameters.

Create Computed Properties

We we still need the radius. We could make a method to do this, but there is another way. Add the following code between the pizzaArea() and the var pizzaType="Cheese" declaration.

var radius : Double {  //computed property
    get{   //must define a getter
        return pizzaDiameter/2.0
    }
    set(newRadius){ //optionally define a setter
        pizzaDiameter = newRadius * 2.0
    }
}    

var area :  Double {
    get{
        return pizzaArea()
    }
}

Line 1 and line 10 declares a computed property, a property based on the values of other properties. Computed properties must declare their type, and must include a getter. Within the block after the declaration, we declare a getter named get which returns a value computed by the already declared property pizzaDiameter. Optionally you can include a setter for the property, as we did in line 5 for the radius. In our second example, we made a second computed property with only a getter for the area, which uses a method in our class.

Use the Model in the Controller

We can now use our model in our code. Before we do let’s write a handler for the clear button. Change clearDisplayButton to this:

 @IBAction func clearDisplayButton(sender : UIButton) {
        resultsDisplayLabel.text = clearString
    }

For our initial setup change viewDidLoad to this:

    override func viewDidLoad() {
        super.viewDidLoad()
        resultsDisplayLabel.text = clearString
        view.backgroundColor = UIColor(red:0.99,green:0.9,blue:0.9,alpha:1.0)
    }

Just under the view controller’s class declaration add the following:

let pizza = Pizza()
let clearString = "I Like Pizza!"

Line 2 is a simple string constant declaration used for clearing the label. Line 1 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 constants. This works, and keeps better memory allocation than using var. Now add a display method for the label:

    func displayPizza(){
        let displayString = String(format:"%6.1f inch %@ Pizza",pizza.pizzaDiameter, pizza.pizzaType)
        resultsDisplayLabel.text = displayString
    }

In line 2, we use dot notation to use the properties of the pizza instance. We do the same for methods as well. Add the following code for the size button:

 @IBAction func sizeButton(sender : UIButton) {
        pizza.pizzaDiameter = pizza.diameterFromString(sender.titleLabel!.text!)
        displayPizza()
    }

The size button reads the title of the button called sender, and assigns the correct size for a pizza to pizza. Line 2 calls in pizza the method diameterfromString() through dot notation.

Now add the handler for selecting a pizzaType:

@IBAction func pizzaType(sender : UISegmentedControl) {
let index = sender.selectedSegmentIndex
pizza.pizzaType = sender.titleForSegmentAtIndex(index)!
displayPizza()
}

Line 2 gets the index of the segment selected. Line 3 get a string with the type of pizza from the segement’s label. You’ll notice the ! after the function here and in line 2 of the sizeButton code. This is a forced unwrap operator. For this lesson, just put them in – I’ll explain them in the next lesson
We have gotten everything into place, and can now build and run.

2015-09-01_06-57-20

We didn’t display anything for the area like we did in the first computer. Next time we will add some more functionality by using some collection types to our pizza computer to sell pizza by the square inch so we can calculate quickly how much to sell a pizza for.

The Whole Code

I should have made this two files: one for the model and one for the controller. However, since Swift lets me put them in one file, I did that to make copying and pasting a bit easier.

//
//  ViewController.swift
//  pizzaDemo version 2
//  adds a model class to demonstrate class
//
//  Created by Steven Lipton on 6/8/14.
//  Revised by Steven Lipton  on 9/1/15 for Swift 2.0 (Beta 4 tested) 
//  Copyright (c) 2014 Steven Lipton. All rights reserved.
//
//

import UIKit

/* --------

Our model for MVC
keeps data  and calculations
about pizzas

note: for ease in copying I left this in one file
you can make a separate file and use import instead.

------------*/
class Pizza
{
    let pi = 3.1415926
    let maxPizza = 24.0

    var pizzaDiameter = 0.0
    var pizzaType = "Cheese"

    var radius : Double {  //computed property
    get{   //must define a getter
        return pizzaDiameter/2.0
    }
    set(newRadius){ //optionally define a setter
        pizzaDiameter = newRadius * 2.0
    }
    }

    var area :  Double {
    get{
        return pizzaArea()
    }
    }

    func pizzaArea() -> Double{
        return radius * radius * pi
    }

    func diameterFromString(aString:String) -> Double {
        switch aString {
        case "Personal":
            return 8.0
        case "10\"":
            return 10.0
        case "12\"":
            return 12.0
        case "16\"","15\"":
            return 16.0
        case "18\"":
            return 18.0
        case "24\"":
            return 24.0
        default:
            return 0.0
        }
    }
}

/*----------

The View Controller

-----------------*/

class ViewController: UIViewController {

    let pizza = Pizza()
    let clearString = "I Like Pizza!"

    @IBOutlet var resultsDisplayLabel : UILabel!

    func displayPizza(){
        let displayString = String(format:"%6.1f inch %@ Pizza",pizza.pizzaDiameter, pizza.pizzaType)
        resultsDisplayLabel.text = displayString
    }


    @IBAction func pizzaType(sender : UISegmentedControl) {
        let index = sender.selectedSegmentIndex
        pizza.pizzaType = sender.titleForSegmentAtIndex(index)!
        displayPizza()
    }

    @IBAction func sizeButton(sender : UIButton) {
        pizza.pizzaDiameter = pizza.diameterFromString(sender.titleLabel!.text!)
        displayPizza()
    }

    @IBAction func clearDisplayButton(sender : UIButton) {
        resultsDisplayLabel.text = clearString
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        resultsDisplayLabel.text = clearString
        view.backgroundColor = UIColor(red:0.99,green:0.9,blue:0.9,alpha:1.0)
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

}

13 thoughts on “The Swift Swift Tutorial: Adding an MVC Model Class in Swift”

  1. Thanks for the tutorial. Just one thing to point out in case other’s have the same problem, I couldn’t get sender.titleLabel.text to work. After a lot of googling I found a solution like this:
    if let text = sender.textLabel.text {
    pizza.pizzaDiameter = pizza.diameterFromString(text)
    }

    I’m new to iPhone development and especially swift, so if this is wrong, let me know. But at least it got me past this part to move on the the rest of the tutorial.

    1. It’s not wrong — good catch and good job at research to fix it. Welcome to the joys of Beta Swift. Apple has been changing a lot of the API to standardize it for Swift, and one thing they have done particularly since beta 7 and the GM seed is change many of the implicit optionals to explicit. What you wrote is one way of getting the right answer, though a bit verbose. It does have a few places where it’s very useful — it’s the only way to get a UISplitViewController to work.

      Both the titlelabel and text are optionals, so both need to be forced into value. The code you found is one of the gentle ways of doing that. The one I use is the short but brutal way.

      @IBAction func sizeButton(sender : UIButton) {
              pizza.pizzaDiameter = pizza.diameterFromString(sender.titleLabel!.text!)
              displayPizza()
          }

      Check out my tutorial on optionals Ten Points for Using Optionals. to understand the difference between the two and My post Using Dictionaries and Optional Types for an introduction to these pesky things called optionals. I also posted a longer explanation as my latest blog post

      Enjoy your journey into the world of iPhone development and Swift.

  2. It is nicely explain…I am facing the problem with the Lay-out of the Pizza size buttons which are in one row. In the Simulator some buttons going out of the frame..I tried several constrains and also tried UIView but no help…Can you demonstrate please…Thanks

    1. Probably it is the size classes that’s the problem. if you look at the bottom of the storybaord pane, you should see w:Any h:Any. Click it and change the size class to this:
      Make a compact width size class

      Clear out all your constraints and you should be fine.

      1. Thanks for the answer….appreciated. It is working.
        Just for the suggestion, It would be very great if you come with one tutorial with all aspects of Auto-Layout and Constrains in swift with different kind of cases of layout, as I was searching several resources to solve this Issue, I came across several aspects of Auto layout and constrains…several sources describe basic knowledge on outlet, but very few sources described the real practical cases like in above code….Thanks

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