Extensions
Prerequisites: Protocols
An extension adds new functionality to an existing type — including
types you didn't write, such as Int or String.
Adding to built-in types
extension Int {
var isEven: Bool { self % 2 == 0 }
func times(_ action: () -> Void) {
for _ in 0..<self { action() }
}
}
extension String {
var initials: String {
split(separator: " ").compactMap(\.first).map(String.init).joined()
}
}
print(4.isEven)
3.times { print("hi") }
print("Grace Brewster Hopper".initials)
true
hi
hi
hi
GBH
Organizing your own types
Extensions are widely used to split a type into readable sections — for example, one extension per protocol conformance:
struct Money {
let cents: Int
}
extension Money: CustomStringConvertible {
var description: String {
"$\(cents / 100).\(String(format: "%02d", cents % 100))"
}
}
(String(format:) comes from Foundation.)
Putting a custom init in an extension also keeps the struct's automatic
memberwise initializer.
Protocol extensions — default implementations
Extending a protocol gives every conforming type a method for free:
protocol Describable {
var name: String { get }
}
extension Describable {
func describe() -> String { "This is \(name)" }
}
struct Dog: Describable { let name: String }
struct City: Describable { let name: String }
print(Dog(name: "Rex").describe())
print(City(name: "Oslo").describe())
This is Rex
This is Oslo
This combination — protocols plus default implementations — is the heart of protocol-oriented programming, covered in Level 3.
Limits
Extensions can add methods, computed properties, initializers, nested types, and protocol conformances. They can't add stored properties.
Common mistakes
- Adding very general names to built-in types (
extension String { var value ... }). Keep extension names specific, so they don't clash with other code or future Swift versions.