Inheritance

Inheritance lets one class build on another, reusing its behaviour and adding or refining it. It is the feature that most distinguishes classes from the other named types in Swift. In this chapter you'll define base classes, create subclasses, override what you inherit, reach back into a superclass with super, and lock behaviour down with final.

Note: Only classes inherit in Swift. Structures and enumerations are value types and have no inheritance — they gain shared behaviour through protocols and extensions instead.

Defining a base class

A base class is any class that does not inherit from another class. Swift classes don't descend from a universal root the way some languages require, so a class with no superclass simply stands on its own. Here is a base class describing any vehicle:

class Vehicle {
    var currentSpeed = 0.0

    var description: String {
        return "travelling at \(currentSpeed) km/h"
    }

    func makeNoise() {
        // Does nothing — an arbitrary vehicle has no characteristic noise.
    }
}

let someVehicle = Vehicle()
print("Vehicle: \(someVehicle.description)")
// → Vehicle: travelling at 0.0 km/h

A stored property, a read-only computed property, and a method that subclasses can specialise.

Subclassing

Subclassing bases a new class on an existing one. The subclass inherits every characteristic of its superclass and can add more. You indicate the superclass after a colon:

class Bicycle: Vehicle {
    var hasBasket = false
}

let bicycle = Bicycle()
bicycle.hasBasket = true
bicycle.currentSpeed = 15.0   // inherited from Vehicle
print("Bicycle: \(bicycle.description)")
// → Bicycle: travelling at 15.0 km/h

Subclasses can themselves be subclassed, building a chain of behaviour:

class Tandem: Bicycle {
    var currentNumberOfPassengers = 0
}

let tandem = Tandem()
tandem.hasBasket = true            // from Bicycle
tandem.currentNumberOfPassengers = 2  // from Tandem
tandem.currentSpeed = 22.0          // from Vehicle
print("Tandem: \(tandem.description)")
// → Tandem: travelling at 22.0 km/h

Overriding

A subclass can provide its own implementation of an instance method, type method, instance property, type property, or subscript that it would otherwise inherit. This is called overriding. You must prefix the new version with the override keyword; Swift checks that a matching declaration genuinely exists in the superclass, catching typos at compile time.

Overriding methods

class Train: Vehicle {
    override func makeNoise() {
        print("Choo Choo")
    }
}

let train = Train()
train.makeNoise()
// → Choo Choo

Overriding properties

You can override an inherited property to provide your own getter (and setter), or to add observers. You must restate the property's name and type so Swift can confirm it matches the superclass. A read-only inherited property may be overridden as read-write, but a read-write property cannot be narrowed to read-only.

class Car: Vehicle {
    var gear = 1

    override var description: String {
        return super.description + " in gear \(gear)"
    }
}

let car = Car()
car.currentSpeed = 80.0
car.gear = 3
print("Car: \(car.description)")
// → Car: travelling at 80.0 km/h in gear 3

Overriding property observers

You can add willSet and didSet observers to an inherited property regardless of how it was originally implemented. You cannot, however, add observers to a constant stored property or to a read-only computed property — neither can change after it is set.

class AutomaticCar: Car {
    override var currentSpeed: Double {
        didSet {
            gear = Int(currentSpeed / 10.0) + 1
        }
    }
}

let automatic = AutomaticCar()
automatic.currentSpeed = 35.0
print("AutomaticCar: \(automatic.description)")
// → AutomaticCar: travelling at 35.0 km/h in gear 4

Accessing the superclass with super

Inside an override you can reach the version you replaced through the super prefix. This is how you extend behaviour rather than discard it:

In the Car.description example above, super.description produces the base sentence and the subclass appends to it — composing behaviour instead of duplicating it.

Preventing overrides with final

Mark a member with final to forbid subclasses from overriding it: final var, final func, final class func, or final subscript. Marking the whole class final prevents it from being subclassed at all. Attempting to override anything final is a compile-time error.

final class Spaceship: Vehicle {
    final func launch() {
        print("Lift off!")
    }
}

// class Shuttle: Spaceship { }  // ❌ error: cannot inherit from final class

Tip: Beyond preventing accidental overrides, final helps the compiler devirtualise calls — it can dispatch them directly instead of through a method table — which can make hot code paths faster. Reach for it when a class or method is not designed to be extended.

Wrapping up

Inheritance gives classes a way to share and refine behaviour: define a base class, subclass it, override the parts you want to change, call back into super to build on what you inherited, and use final to seal off anything that shouldn't be extended. Next we'll look at initialization — how instances of these classes get their stored properties set up correctly, including the special rules that apply when inheritance is in play.