Extensions

Swift · Practical · 8 min

Extensions organize code and let you add conformances to types you don't own, such as Int or Foundation types.

An extension adds functionality to an existing type — including types you didn't write, like Int, String or URL:

extension Int {
    var isEven: Bool { self % 2 == 0 }
}

Extensions can add computed properties, methods (mutating on value types), initializers, subscripts, nested types and protocol conformances. They cannot add stored properties or override existing members.

Two common uses:

  • Retroactive modeling: make a type conform to your protocol after the fact, e.g. extension Date: MyFormattable {}. In Swift 6 the compiler warns when you conform a type you don't own to a protocol you don't own either; mark it @retroactive if you accept the risk of clashing with the owner later.
  • Code organization: put each conformance in its own extension, extension ProfileView: UITableViewDataSource { ... }, so the file reads in sections.

Adding an initializer in an extension of a struct keeps the compiler-generated memberwise initializer, which a custom init in the main body would remove.

Constrained extensions add members only when a condition holds: extension Array where Element: Numeric { func sum() -> Element { reduce(0, +) } }. This is how generic code gains abilities when its type arguments allow it.

Members added in extensions follow normal access control, and private members are visible to other extensions of the type in the same file.

Example

extension Int {
    var isEven: Bool { self % 2 == 0 }
    func times(_ body: () -> Void) {
        for _ in 0..<self { body() }
    }
}

extension Array where Element: Numeric {
    func sum() -> Element { reduce(0, +) }
}

struct Celsius { var value: Double }
extension Celsius {
    init(fahrenheit: Double) { value = (fahrenheit - 32) * 5 / 9 }
}
extension Celsius: CustomStringConvertible {
    var description: String { "\(value)°C" }
}

print(4.isEven)                       // true
3.times { print("hi", terminator: " ") }  // hi hi hi
print([1.5, 2.5].sum())               // 4.0
print(Celsius(fahrenheit: 212))       // 100.0°C
print(Celsius(value: 5))              // 5.0°C (memberwise init kept)

Remember

  • Extensions add behavior and conformances, not stored properties
  • Constrained extensions (where Element: P) add members conditionally
  • An init in an extension keeps the memberwise initializer

Watch out for

  • Adding broad extensions on standard types that clash with names from other modules.

Check yourself

  1. Which of these can an extension NOT add?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.swift.org/swift-book/documentation/the-swift-programming-language/extensions/

Builds on

Unlocks

Nothing builds on this yet.