Extensions

Extensions add new functionality to an existing class, structure, enumeration, or protocol type — even types you didn't write and don't have the source for, like Int or String. They let you grow a type's capabilities without subclassing, and they're a cornerstone of how idiomatic Swift is organized.

Extension syntax

You declare an extension with the extension keyword, followed by the name of the type you're extending.

extension SomeType {
    // new functionality goes here
}

// An extension can also add protocol conformance:
extension SomeType: SomeProtocol, AnotherProtocol {
    // implementation of protocol requirements
}

Note: an extension can add new functionality to a type, but it can never add stored properties or property observers to an existing type. It also can't override existing functionality. Everything an extension adds is purely additive.

Computed properties

Extensions can add computed instance and type properties. Here we add unit-conversion accessors to the built-in Double type.

extension Double {
    var km: Double { self * 1000.0 }
    var m:  Double { self }
    var cm: Double { self / 100.0 }
    var mm: Double { self / 1000.0 }
}

let marathon = 42.195.km + 195.m
print("A marathon is \(marathon) metres long")
// → A marathon is 42390.0 metres long

These are computed, not stored — which is why an extension can add them. 3.cm simply computes 0.03 each time it's read.

Initializers

Extensions can add new initializers, letting you accept your own types for an existing type's initialization. For classes, extensions can add convenience initializers but not designated initializers or deinitializers.

struct Size { var width = 0.0, height = 0.0 }
struct Point { var x = 0.0, y = 0.0 }
struct Rect { var origin = Point(), var size = Size() }

extension Rect {
    init(center: Point, size: Size) {
        let originX = center.x - (size.width / 2)
        let originY = center.y - (size.height / 2)
        self.init(origin: Point(x: originX, y: originY), size: size)
    }
}

let centered = Rect(center: Point(x: 4, y: 4),
                     size: Size(width: 3, height: 3))
print(centered.origin)   // → Point(x: 2.5, y: 2.5)

Tip: adding an initializer in an extension (rather than in the original struct declaration) preserves the automatic memberwise initializer. If you'd written the custom init inside the struct itself, you'd lose the free Rect(origin:size:) — extensions let you have both.

Methods and mutating methods

Extensions add instance and type methods. An instance method on a value type (struct or enum) that needs to modify self must be marked mutating, exactly as in the original definition.

extension Int {
    func repetitions(task: () -> Void) {
        for _ in 0..<self {
            task()
        }
    }

    mutating func square() {
        self = self * self
    }
}

3.repetitions { print("Hello!") }
// → Hello! (printed three times)

var value = 5
value.square()
print(value)   // → 25

Subscripts

Extensions can add new subscripts to an existing type. Here we add a decimal-digit subscript to Int.

extension Int {
    subscript(digitIndex: Int) -> Int {
        var decimalBase = 1
        for _ in 0..<digitIndex {
            decimalBase *= 10
        }
        return (self / decimalBase) % 10
    }
}

print(746381295[0])   // → 5  (ones place)
print(746381295[2])   // → 2  (hundreds place)

Nested types

Extensions can add new nested types to existing classes, structs, and enums.

extension Int {
    enum Kind {
        case negative, zero, positive
    }

    var kind: Kind {
        switch self {
        case 0:          return .zero
        case let x where x > 0: return .positive
        default:        return .negative
        }
    }
}

print((-7).kind)   // → negative

Adding protocol conformance with an extension

One of the most common uses of extensions is to make an existing type conform to a protocol. This keeps the conformance — and its requirements — grouped in one focused block, which is great for readability.

protocol TextRepresentable {
    var textualDescription: String { get }
}

struct Dice { let sides: Int }

extension Dice: TextRepresentable {
    var textualDescription: String {
        "A \(sides)-sided die"
    }
}

let d6 = Dice(sides: 6)
print(d6.textualDescription)   // → A 6-sided die

Note: if a type already satisfies all of a protocol's requirements but hasn't been declared to adopt it, you can declare conformance with an empty extension: extension SomeType: SomeProtocol {}.

Extensions on protocols themselves can supply default implementations of requirements — a powerful technique covered in depth in the Protocols lesson.

Wrapping up

Extensions let you grow types after the fact with computed properties, initializers, methods, subscripts, nested types, and protocol conformance — all without subclassing and without touching the original source. They can't add stored properties or override existing behavior, but within those bounds they're one of the most-used tools for keeping Swift code modular and well organized.