Make App Pie

Training for Developers and Artists

A Swift Tutorial to Working with Classes Part 1:Basic Classes

pizza-classI find a lot of confusion among beginners and a handful of intermediate developers about what is a class. Object oriented languages like Swift, Objective C or C++ use classes to organize everything in the language and the API’s. You might have heard words like callback, override, setter and getter and have no idea what they mean.  Have you ever wonder what’s really going on in viewDidLoad?  In this  three-part lesson I’m going to explain what all this means.

We’ll use the playground to introduce classes. This will give us more immediate results to our coding.  Open Xcode,  and make a new playground named ClassPlayground.

 2015-07-16_05-20-22

Once in the playground, add the following:

// a basic class for a pizza
class pizza{
}

This is a basic class. We also call classes objects. Almost everything we use to develop apps is a class, such as Array, String, UIColor or UILabel. If we think of a physical object, there is  a way of describing the object: a description of the object and what it does. If I ordered a pizza, I might order a 10 inch wheat crust BBQ Chicken Pizza. A pizza would have a diameter, crust type, and several toppings. When discussing classes,  we call these properties. Properties are variables and constants we use to describe the class. Change the pizza class to:

class Pizza{
// a basic class for a pizza

    //MARK: Properties
    var diameter:Double = 0.0
    var crust:String = ""
    var toppings:[String] = []
}

Our pizza class now has three properties. First we have a Double variable diameter that will give us the diameter of the pizza. Secondly we have a string where we can describe either whole wheat or white crust. Finally we have a string array of toppings.

Under the class in the playground add this:

let myPizza = Pizza()
let myFriendsPizza = Pizza()

By using our class name Pizza with parentheses after it, we created two constant objects, myPizza and myFriendsPizza of class pizza. We call each of these objects an instance of Pizza. If you look to the right of these statements, you’ll see that we have two blank instances:

2015-07-16_06-01-28

Swift has allocated some memory and placed our blank default from our variable assignments in the object. This process is known as instantiation. Swift does not have to allocate the memory immediately. Sometimes as a good memory practice,  we allocate the memory for an object only  when we first use it, known as lazy instantiation. We only declare its existence with the assignment of the class. When we use a property, we allocate memory.

We can change our properties using dot notation. We give the object name, a dot and then the property. Add this code under the assignment:

myPizza.diameter = 10 //10 inch 25.4 cm pizza
myPizza.crust = "White"
myPizza.toppings = ["Chicken","BBQ Sauce","Cheese","Red Onions"]

You’ll notice we have set values to the properties of myPizza.

2015-07-16_06-13-38

Now do the same to myFriendsPizza:

myFriendsPizza.diameter = 12 //12 inch 30.48cm pizza
myFriendsPizza.crust = "Wheat"
myFriendsPizza.toppings = ["Cheese","Pepperoni"]

That pizza has the values

2015-07-16_06-14-08

We can also have the pizza tell us, or get, values. For example type this:

print(myPizza.diameter)
print(myFriendsPizza.toppings)

We get this:

2015-07-16_06-22-25

We printed the diameter of myPizza and the toppings of myFriendsPizza. For users, printing out the array is not very user-friendly.  We might want to have the toppings print out in a more understandable form as a string.  Add this to our Pizza Class:

//MARK: Methods
func toppingsString()->String{
    var myString = ""
        for topping in toppings{
            myString = myString + topping + " "
        }
    return myString
}

This function loops through all the toppings in the array toppings, storing them in an array myString. It returns the string once done.

Functions found inside of classes are called methods. Methods are the actions the object can take.  We converted an array to a string we could display in a UILabel. Try out the new method by adding this to the bottom of the playground:

2015-07-16_06-48-03

The method makes a string. Let’s add two more methods to our Pizza class:

     func area() -> Double{
        return diameter * diameter * M_PI
    }
    func price(costPerSquareUnit:Double)-> Double{
        return area() * costPerSquareUnit
    }

I do something silly here, but it works for our lesson. I figure out the price of the pizza by multiplying the area of the pizza by a cost per square unit. I’m working in inches and U.S. Dollars for my units.  I figure a BBQ Chicken pizza might have a price of two cents per square inch and a sausage pizza might have a price of one and a half cents per square inch. This however requires an area calculation, so I made another method to calculate the area.

Test the area() method like this:

2015-07-16_07-03-17

Test the price function like this:

2015-07-16_07-03-34

Subclassing and Overrides

Suppose the pizza restaurant  also makes deep dish pizza. Since a  deep dish pizza is thicker than the flat pizza we’ve defined, an area alone is not the best calculation of a pizza price. We are using a lot more ingredients to fill the volume. Instead of computing area, we need to compute volume and multiply that with costPerSquareUnit. We could re-write the entire class, but we don’t have to. We need exactly the same properties as we did before, plus one more for the depth of the pan. Instead of rewriting our Pizza class, we will subclass Pizza, and make a new class DeepDishPizza. At the bottom of your playground code add this:

class DeepDishPizza:Pizza{
}

We defined the class DeepDishPizza and added after it a colon and the name of the class we subclassed. Now type this:

let annahsPizza = DeepDishPizza()
annahsPizza.diameter = 8.0
annahsPizza.crust = "White"
annahsPizza.toppings = ["Cheese","Sausage","Pepperoni"]
annahsPizza.toppingsString()
annahsPizza.area()
annahsPizza.price(0.25)

Everything we defined in Pizza works in DeepDishPizza. We say that DeepDishPizza inherits from Pizza. Pizza is the parent class or superclass of DeepDishPizza. DeepDishPizza is the child class or subclass of Pizza. We say we subclassed Pizza to make DeepDishPizza.

We can also add some new things to the subclass. For example we can add a new property panDepth

class DeepDishPizza:Pizza{
    var panDepth = 4
}

The child class has a property with a default pan depth of four inches. You have enough information to  calculate the pizza volume. Add a new method to the DeepDishPizza class:

func volume() -> Double{
        return area() * panDepth
    }

We used the inherited class method area() and multiplied that value by our panDepth. Now try the new method:

annahsPizza.volume()

You’ll see the volume of the pizza.
2015-07-17_05-19-15

We want to calculate price on a deep dish pizza by the volume not the area. We also want to use the same method price that we used for the superclass in DeepDishPizza for consistency sake. If you’ve worked enough with functions you know you can’t name two functions with the same identifier and parameters. The compiler would complain with a critical error. Subclasses, however, can override methods in a superclass. The keyword override allows us to make a new definition for the method price.
Add this method to your DeepDishPizza class:

override func price(costPerSquareUnit: Double) -> Double {
        return volume() * costPerSquareUnit
    }

And now the price method  of DeepDishPizza calculates for volume:
2015-07-17_05-42-54

Sometimes, we don’t need to override a method. Instead we use different parameters. For example:

    func price(costPerSquareUnit:Double, panDepth:Double) -> Double{
        self.panDepth = panDepth
        return volume() * costPerSquareUnit
    }

In this method we set our panDepth in the price. We have two parameters instead of one. Swift see this as a completely different function, and thus we don’t need the override keyword.

Notice in this function the following line:

self.panDepth = panDepth

We’ll discuss more about scope in the next part, but our parameter name is the same as our property name. Swift assumes that the identifier refers to the last defined identifier of that name.  panDepth means the parameter panDepth,not the property panDepth.  For us to tell Swift to we want the property panDepth, we use the self keyword. The keyword self tells the compiler we are talking about a property or method in this class. self means “this instance.” The statement above thus assigns the value of the parameter as the value of the property.

Class Methods

The methods we have been working with are instance methods. To use one, you define an instance of a class and refer to the instance to run the method. There is also class Methods, which need only the class name. The most common use for class methods is for initializing our properties when we create an instance. Often you’ll hear of such class methods as initializers or constructors. For example, add the following to our Pizza class, just above the instance methods:

    //MARK: Class Methods (initializers)
    init(diameter:Double, crust:String, toppings:[String]){
        self.diameter = diameter
        self.crust = crust
        self.toppings = toppings
    }

This is a method, but we do not use func to start it. We don’t even have a name for the method. Instead you use the keyword init, followed by your parameter list. This code, like many constructors, assigns values to our properties. You’ll notice that you have a few errors in the playground. Once you define a class method, Swift’s automatic assumptions about initializing methods shuts down. Of course the bugs inherit like everything else in a subclass.  We have an error for DeepDishPizza too. When you start defining class methods, always make one method above all your other constructors named init with no parameters:

init(){}

When you add this to the Pizza class, the errors disappear. Now under myFriendsPizza You can define darrylsPizza .

let darrylsPizza = Pizza(
    diameter: 14,
    crust: "Wheat",
    toppings: ["Fresh Mozzarella","Tomatoes"])

and see that it sets properties correctly.
2015-07-17_06-25-04

You can of course add class methods to subclasses, with one wrinkle. Add this to the DeepDishPizza class:

    //MARK: Class Methods
    override init(){
        super.init()
    }

Here’s our parameterless init. It has one line of code. In order to inherit from the superclass you have to call the superclass’s init. If you don’t, you will get an error. The keyword super gives us a reference to the superclass, where we can call its init method.

We can add parameters to subclass’  class methods, but always   initialize the superclass first. Here’s two examples:

    init(panDepth:Double){
        super.init()
        self.panDepth = panDepth
    }

    init(diameter: Double, crust: String, toppings: [String], panDepth:Double) {
        super.init(diameter: diameter, crust: crust, toppings: toppings)
        self.panDepth = panDepth
    }

In init(panDepth:Double) we set the pan depth only, and leave the default values for the superclass. In the second case, we set all of our properties. Here we use super to initialize the properties defined in the superclass with its initializer that has parameters, while setting child class property panDepth directly.

Once added test these out, and we get this:

2015-07-17_06-48-04

That’s enough to comprehend in one post. We’ll continue next week with part two. We’ll learn a little more about Scope, Setters and getters. After that, in part three We’ll learn about protocols and abstract classes.

The Whole Code

//: Playground - noun: a place where people can play
//*****************************************
// Classes Lesson 1 playground
// By Steven Lipton, MakeAppPie.com
// (C)2015 MakeAppPie.com
//*****************************************
import UIKit
class Pizza{
// a basic class for a pizza

    //MARK: Properties
    var diameter:Double = 0.0
    var crust:String = ""
    var toppings:[String] = []

    //MARK: Class Methods
    init(){}
    init(diameter:Double, crust:String, toppings:[String]){
        self.diameter = diameter
        self.crust = crust
        self.toppings = toppings
    }

    //MARK: Methods
    func toppingsString()->String{
        var myString = ""
        for topping in toppings{
            myString = myString + topping + " "
        }
        return myString
    }
    func area() -> Double{
        return diameter * diameter * M_PI
    }
    func price(costPerSquareUnit:Double)->Double{
        return self.area() * costPerSquareUnit
    }
}

// Test initializers
let myPizza = Pizza()
let myFriendsPizza = Pizza()
let darrylsPizza = Pizza(
    diameter: 14,
    crust: "Wheat",
    toppings: ["Fresh Mozzarella","Tomatoes"])
// Test the instance methods
myPizza.diameter = 10 //10 inch 25.4 cm pizza
myPizza.crust = "White"
myPizza.toppings = ["Chicken","BBQ Sauce","Cheese","Red Onions"]

myFriendsPizza.diameter = 12 //12 inch 30.48cm pizza
myFriendsPizza.crust = "Wheat"
myFriendsPizza.toppings = ["Cheese","Pepperoni"]
// I placed these in print functions
// In playgounds you don't have to do that,
// since the values show up to the right

print(myPizza.diameter)
print(myFriendsPizza.toppings)

print(myFriendsPizza.toppingsString())
print(myPizza.toppingsString())

print (myPizza.area())
print(myFriendsPizza.area())

print(myPizza.price(0.02))
print(myPizza.price(0.015))

//*****************************
// The subclass DeepDishPizza
//*****************************

class DeepDishPizza:Pizza{
    // A subclass of Pizza with a pan depth.
    //price is computed by volume
    //MARK: Properties
    var panDepth:Double = 4.0
    //MARK: Class Methods
    override init(){
        super.init()
    }

    init(panDepth:Double){
        super.init()
        self.panDepth = panDepth
    }

    init(diameter: Double, crust: String, toppings: [String], panDepth:Double) {
        super.init(diameter: diameter, crust: crust, toppings: toppings)
        self.panDepth = panDepth
    }

    //MARK: Instance Methods
    func volume() -> Double{
        return area() * panDepth
    }

    override func price(costPerSquareUnit: Double) -> Double {
        return volume() * costPerSquareUnit
    }

    func price(costPerSquareUnit:Double, panDepth:Double) -> Double{
        self.panDepth = panDepth
        return volume() * costPerSquareUnit
    }
}

// Test Class methods
let annahsPizza = DeepDishPizza()
let jillsPizza = DeepDishPizza(panDepth: 3.0)
let flosPizza = DeepDishPizza(
    diameter: 10,
    crust: "White",
    toppings: ["Cheese"],
    panDepth: 3.0)

//test instance methods
// didn't use print here
annahsPizza.diameter = 8.0
annahsPizza.crust = "White"
annahsPizza.toppings = ["Cheese","Sausage","Pepperoni"]
annahsPizza.toppingsString()
annahsPizza.area()
annahsPizza.volume()
annahsPizza.price(0.023)

6 responses to “A Swift Tutorial to Working with Classes Part 1:Basic Classes”

  1. […] our first part we looked at some of the basics of classes in Swift. In this part, we look at some of the more […]

  2. […] the first tutorial in this series we introduced classes. In  the second we did some more advanced manipulations of […]

  3. It is not always true that inherited class must call super first. The rule is that inherited class must initialize all its properties before delegating to super class designated initializer. In your example, since the inherited class property has default value and is a var, it would be considered initialized and hence it is safe to call super unit first.

    1. Yes that will work. I would be so reluctant to do it, since it keeps one out of trouble, that I made it a rule.

      In the demo case of

        init(panDepth:Double){
              super.init()
              self.panDepth = panDepth        
          }

      the property initialized is a child property. What if it was changing an inherited property like crust, in my subclass like this:

        init(panDepth:Double){
              crust = "White"
              super.init()
              self.panDepth = panDepth        
          }

      This will cause an error. Now one can keep track of the inherited and new properties of a subclass, and if one initialized them or not. That’s a lot of work when your hierarchy of classes is five or six classes thick. It always works, is a lot easier to code, more consistent and documents better if the first thing you do is initialize super.init()

      Apple does this in their code. Check out viewDidLoad and some of the other life cycle methods of view controllers. They always, even if they don’t have to, have the super first, and then a comment about adding code.

  4. […] and Computed Properties:A Swift Guide to Working with Classes Part One and Part […]

  5. […] to a new instance. If you haven’t worked with initializers, I suggest you take a look at my tutorial on classes for more information on […]

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 )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: