Skip to content

Extensions

Extensions allow you to add new features to existing types like classes, structs, enums, or protocols. This is useful even if you don't have the original source code—a technique called retroactive modeling. Unlike Objective-C categories, Swift extensions don't need names.

Extensions in Swift can:

  • Add computed properties (instance or static).
  • Define methods (instance or static).
  • Provide new initializers.
  • Add subscripts.
  • Introduce nested types.
  • Make a type conform to protocols.

You can also extend protocols to add default implementations or extra features for conforming types. See the Protocols section for details.

Note: Extensions add new features but cannot override existing ones.

Extension Syntax

Use the extension keyword to declare one:

swift
extension MyType {
    // Add new features here
}

To make a type adopt protocols:

swift
extension MyType: ProtocolA, ProtocolB {
    // Implement protocol requirements here
}

This is covered in Adding Protocol Conformance with an Extension.

Extensions work with generics too—see Extending a Generic Type. You can add conditional features using generic where clauses.

Note: New features from extensions are available on all instances, even those created before the extension.

Computed Properties

Extensions can add computed properties. Here's an example extending Double for basic temperature conversions in Celsius:

swift
extension Double {
    var celsius: Double { return self }
    var fahrenheit: Double { return (self * 9/5) + 32 }
    var kelvin: Double { return self + 273.15 }
}

let roomTempC = 25.0.celsius
print("Room temperature is \(roomTempC)°C")
// Prints "Room temperature is 25.0°C"
let roomTempF = 25.0.fahrenheit
print("That's \(roomTempF)°F")
// Prints "That's 77.0°F"

These properties treat Double as Celsius by default and compute conversions. Use them like this:

swift
let boilingPoint = 100.0.celsius
let inKelvin = boilingPoint.kelvin
print("Boiling point is \(inKelvin)K")
// Prints "Boiling point is 373.15K"

Note: Extensions can't add stored properties or observers to existing ones.

Initializers

Extensions can add convenience initializers but not designated ones or deinitializers—those stay in the original type.

For value types with default property values and no custom initializers, extensions can use the default or memberwise initializers.

If extending a struct from another module, new initializers must call an original initializer before accessing self.

Example with custom structs for a circle:

swift
struct Radius {
    var value = 0.0
}
struct Center {
    var x = 0.0, y = 0.0
}
struct Circle {
    var center = Center()
    var radius = Radius()
}

Default and memberwise initializers:

swift
let defaultCircle = Circle()
let customCircle = Circle(center: Center(x: 1.0, y: 1.0), radius: Radius(value: 4.0))

Extend Circle for an initializer using diameter:

swift
extension Circle {
    init(center: Center, diameter: Double) {
        let radiusValue = diameter / 2
        self.init(center: center, radius: Radius(value: radiusValue))
    }
}

let diameterCircle = Circle(center: Center(x: 3.0, y: 3.0), diameter: 10.0)
// Diameter 10.0 means radius 5.0

Note: Ensure full initialization in new initializers.

Methods

Extensions add instance or static methods. Example: Add a method to Int to repeat a task:

swift
extension Int {
    func repeatAction(action: () -> Void) {
        for _ in 0..<self {
            action()
        }
    }
}

4.repeatAction {
    print("Repeat!")
}
// Repeat!
// Repeat!
// Repeat!
// Repeat!

Mutating Instance Methods

Methods that change self must be mutating for structs/enums. Example: Add a method to cube an Int:

swift
extension Int {
    mutating func cube() {
        self = self * self * self
    }
}

var number = 2
number.cube()
// number is now 8

Subscripts

Add subscripts for custom access. Example: Subscript on Int to get binary digits from the right:

swift
extension Int {
    subscript(binaryIndex: Int) -> Int {
        var base = 1
        for _ in 0..<binaryIndex {
            base *= 2
        }
        return (self / base) % 2
    }
}

13[0]  // 1 (binary 1101)
13[1]  // 0
13[2]  // 1
13[3]  // 1

If index exceeds bits, returns 0 (as if padded with zeros):

swift
13[4]  // 0

Nested Types

Add nested types inside extensions. Example: Nested enum in Int for parity:

swift
extension Int {
    enum Parity {
        case even, odd
    }
    var parity: Parity {
        self % 2 == 0 ? .even : .odd
    }
}

func printParities(_ numbers: [Int]) {
    for number in numbers {
        switch number.parity {
        case .even:
            print("E ", terminator: "")
        case .odd:
            print("O ", terminator: "")
        }
    }
    print("")
}

printParities([2, 3, 4, 5])  
// Prints "E O E O "

Note: Use shorthand enum cases in switches since type is inferred.

Released under the MIT License.