Model your program

Swift Classes

An introduction to classes in Swift: defining properties and methods, why classes are reference types, writing initializers with self, and inheritance.

This tutorial belongs to the Swift series

Classes look a lot like structures, but they behave differently in a few important ways. Let’s start with what’s the same.

You define a class with the class keyword:

class Dog {

}

Inside it you define stored properties:

class Dog {
    var age = 0
}

A class definition creates a type. You create an instance by calling the type:

let roger = Dog()

And you read properties with the dot syntax:

let roger = Dog()
roger.age // 0

The same syntax updates a property:

roger.age = 9

Instances of a class are called objects, like instances of a struct.

Classes are reference types

Here’s the first big difference. Classes are reference types. Structures and enumerations are value types.

Assigning a class instance to another variable does not copy it. Both variables point to the same object:

class Dog {
    var age = 0
}

let roger = Dog()
let syd = roger

roger.age = 9
// syd.age == 9

Change roger and syd changes too, because there’s only one dog. With a struct, syd would have been an independent copy.

Notice that roger is a let and we still changed its property. With a class, let means the variable always points to the same object. It doesn’t freeze the object’s contents. This surprises people coming from structs, where let freezes everything.

Initializers

With a struct, Swift writes an initializer for you. With a class, if a property has no default value, you must write one yourself:

class Dog {
    var age: Int

    init(age: Int) {
        self.age = age
    }
}

let roger = Dog(age: 9)

Skip the init and the compiler says Class 'Dog' has no initializers.

Notice self. We need it because age is both the property and the parameter name. self.age is the property, plain age is the parameter.

Methods

Classes can have instance methods, functions that belong to an instance:

class Dog {
    var age = 8
    var name = "Roger"

    func bark() {
        print("\(name): wof!")
    }
}

And type methods, marked with static, that belong to the type itself:

class Dog {
    var age = 8
    var name = "Roger"

    func bark() {
        print("\(name): wof!")
    }
    static func hello() {
        print("Hello I am the Dog class")
    }
}

You call it as Dog.hello().

Inheritance

The second big difference: classes support inheritance. A class can take all the properties and methods of another class and add its own.

Say we have an Animal class. Every animal has an age:

class Animal {
    var age: Int
}

Not every animal has a name, but dogs do. So we create a Dog class that inherits from Animal:

class Dog: Animal {
    var name: String
}

Both classes need an initializer. In Dog, we set the class-specific property first, then call the parent’s initializer with super.init():

class Animal {
    var age: Int

    init(age: Int) {
        self.age = age
    }
}

class Dog: Animal {
    var name: String

    init(age: Int, name: String) {
        self.name = name
        super.init(age: age)
    }
}

var horse = Animal(age: 8)
var roger = Dog(age: 8, name: "Roger")

Animal is the superclass, and Dog is a subclass. roger.age works even though Dog never declared age, because it inherited it.

There’s a lot more to say about classes, but this is enough to start. My advice: reach for a struct first, and switch to a class only when you need shared references or inheritance.

Lesson completed