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.