Skip to content

Inheritance

Inheritance allows a class to adopt methods, properties, and other features from another class. The inheriting class is called a subclass, and the class it inherits from is its superclass. This mechanism helps differentiate classes and promotes code reuse in Swift.

Swift ensures overrides are accurate by verifying that they match the superclass definitions. Subclasses can also add observers to inherited properties to detect value changes, whether those properties are stored or computed.

Creating a Base Class

A class without a superclass is a base class.

Note: Unlike some languages, Swift classes don't inherit from a shared universal base. Any class without a specified superclass becomes a base class.

Here's a base class Transport with a stored property currentVelocity (default 0.0, type Double). It includes a read-only computed property description for a string summary, and a makeSound() method that does nothing by default (subclasses can customize it):

swift
class Transport {
    var currentVelocity = 0.0
    var description: String {
        return "moving at \(currentVelocity) km/h"
    }
    func makeSound() {
        // Default: no sound for a generic transport
    }
}

Create an instance like this:

swift
let basicTransport = Transport()
print("Transport: \(basicTransport.description)")
// Output: Transport: moving at 0.0 km/h

This base class outlines general transport traits but needs subclasses for specifics.

Building Subclasses

Subclassing creates a new class based on an existing one, inheriting its features while allowing additions or modifications.

Syntax: class NewSubclass: ExistingSuperclass { /* details */ }

Example subclass Cycle from Transport:

swift
class Cycle: Transport {
    var hasCarrier = false
}

Cycle inherits currentVelocity, description, and makeSound(). It adds hasCarrier (default false, type Bool).

Customize after instantiation:

swift
let myCycle = Cycle()
myCycle.hasCarrier = true
myCycle.currentVelocity = 20.0
print("Cycle: \(myCycle.description)")
// Output: Cycle: moving at 20.0 km/h

Subclasses can be further subclassed. Here's DuoCycle from Cycle:

swift
class DuoCycle: Cycle {
    var passengerCount = 0
}

DuoCycle inherits everything and adds passengerCount:

swift
let duo = DuoCycle()
duo.hasCarrier = true
duo.passengerCount = 1
duo.currentVelocity = 18.0
print("DuoCycle: \(duo.description)")
// Output: DuoCycle: moving at 18.0 km/h

Customizing with Overrides

Subclasses can replace inherited methods, properties, or subscripts with custom versions using the override keyword. This ensures intent and compiler checks for matches.

Use super to access the superclass version in your override, refining or extending it.

  • For methods: Call super.someMethod().
  • For properties: Use super.someProperty in getters/setters.
  • For subscripts: Access super[someIndex].

Overriding Methods

Provide a new implementation for an inherited method.

Example: RailEngine subclass overrides makeSound():

swift
class RailEngine: Transport {
    override func makeSound() {
        print("Toot Toot")
    }
}

Usage:

swift
let engine = RailEngine()
engine.makeSound()
// Output: Toot Toot

Overriding Properties

Customize getters, setters, or add observers to inherited properties.

Custom Getters and Setters

Override any inherited property, regardless of its original type (stored or computed). Match name and type exactly. You can make a read-only property read-write, but not vice versa.

Note: Overrides with setters must include getters. To keep the value unchanged in the getter, return super.someProperty.

Example: WheeledVehicle subclass adds transmission and overrides description:

swift
class WheeledVehicle: Transport {
    var transmission = 1
    override var description: String {
        return super.description + " in transmission \(transmission)"
    }
}

Usage:

swift
let vehicle = WheeledVehicle()
vehicle.currentVelocity = 40.0
vehicle.transmission = 2
print("WheeledVehicle: \(vehicle.description)")
// Output: WheeledVehicle: moving at 40.0 km/h in transmission 2

Adding Property Observers

Observe changes to inherited properties using willSet or didSet, even if originally computed.

Note: Can't add observers to constants or read-only computed properties. If providing a setter, observe changes there instead of separately.

Example: SmartVehicle adjusts transmission based on currentVelocity:

swift
class SmartVehicle: WheeledVehicle {
    override var currentVelocity: Double {
        didSet {
            transmission = Int(currentVelocity / 15.0) + 1
        }
    }
}

Usage:

swift
let smart = SmartVehicle()
smart.currentVelocity = 50.0
print("SmartVehicle: \(smart.description)")
// Output: SmartVehicle: moving at 50.0 km/h in transmission 4

Blocking Overrides

Mark methods, properties, or subscripts as final to prevent subclass overrides (e.g., final func someMethod()). Extensions can also use final.

Mark entire classes as final class to block subclassing. Attempts trigger compile-time errors.

Released under the MIT License.