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:
extension MyType {
// Add new features here
}To make a type adopt protocols:
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:
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:
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:
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:
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:
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.0Note: Ensure full initialization in new initializers.
Methods
Extensions add instance or static methods. Example: Add a method to Int to repeat a task:
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:
extension Int {
mutating func cube() {
self = self * self * self
}
}
var number = 2
number.cube()
// number is now 8Subscripts
Add subscripts for custom access. Example: Subscript on Int to get binary digits from the right:
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] // 1If index exceeds bits, returns 0 (as if padded with zeros):
13[4] // 0Nested Types
Add nested types inside extensions. Example: Nested enum in Int for parity:
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.