Swift Extensions

An extension adds new functionality to an existing type — without modifying its original source code. You can extend types you wrote yourself, Apple's built-in types, or even types from third-party libraries. Think of an extension like adding a new app to a phone: you are not rebuilding the phone, just giving it extra capability.

Basic Extension Syntax

extension Int {
    func squared() -> Int {
        return self * self
    }
}

print(5.squared())    // Output: 25
print(12.squared())   // Output: 144

The keyword self inside an extension refers to the value the method is called on — here, the integer itself.

Diagram: Extension Adds Without Replacing

Original Int type                  After extension
+---------------------------+      +---------------------------+
| + (addition)              |      | + (addition)              |
| - (subtraction)           |  →   | - (subtraction)           |
| * (multiplication)        |      | * (multiplication)        |
| ... (built-in methods)    |      | squared()   ← NEW         |
+---------------------------+      | isEven      ← NEW         |
                                   +---------------------------+

Computed Properties in Extensions

Extensions can add computed properties, but not stored properties.

extension Int {
    var isEven: Bool { self % 2 == 0 }
    var isOdd: Bool  { self % 2 != 0 }
    var doubled: Int { self * 2 }
}

print(6.isEven)    // Output: true
print(7.isOdd)     // Output: true
print(4.doubled)   // Output: 8

Extending Double

extension Double {
    var asCurrency: String {
        return String(format: "$%.2f", self)
    }

    var asPercentage: String {
        return String(format: "%.1f%%", self * 100)
    }
}

print(9.99.asCurrency)    // Output: $9.99
print(0.175.asPercentage) // Output: 17.5%

Extending String

extension String {
    var wordCount: Int {
        return self.split(separator: " ").count
    }

    func repeat(_ times: Int) -> String {
        return String(repeating: self, count: times)
    }

    var isPalindrome: Bool {
        let clean = self.lowercased().filter { $0.isLetter }
        return clean == String(clean.reversed())
    }
}

print("Hello world Swift".wordCount)   // Output: 3
print("ha".repeat(3))                  // Output: hahaha
print("racecar".isPalindrome)          // Output: true
print("swift".isPalindrome)            // Output: false

Extending Your Own Types

Extensions keep large types organized by splitting functionality into logical sections.

struct Rectangle {
    var width: Double
    var height: Double
}

// Core math in an extension
extension Rectangle {
    var area: Double      { width * height }
    var perimeter: Double { 2 * (width + height) }
    var isSquare: Bool    { width == height }
}

// Display in a separate extension
extension Rectangle {
    func describe() {
        print("Rectangle \(width)×\(height) | Area: \(area)")
    }
}

let r = Rectangle(width: 5, height: 3)
r.describe()
print(r.isSquare)   // Output: false

Adding Initializers via Extension

You can add new initializers to a struct through an extension. This preserves the automatic memberwise initializer Swift generates.

struct Point {
    var x: Double
    var y: Double
}

extension Point {
    init(at distance: Double, angle: Double) {
        self.x = distance * cos(angle)
        self.y = distance * sin(angle)
    }

    static var origin: Point { Point(x: 0, y: 0) }
}

let p = Point(at: 5.0, angle: 0.0)
print(p.x)   // Output: 5.0

let o = Point.origin
print(o.x)   // Output: 0.0

Protocol Conformance via Extension

You can make an existing type conform to a protocol entirely inside an extension. This separates concerns cleanly.

protocol Printable {
    func prettyPrint()
}

struct Invoice {
    var id: Int
    var total: Double
}

extension Invoice: Printable {
    func prettyPrint() {
        print("Invoice #\(id) — Total: \(total.asCurrency)")
    }
}

let inv = Invoice(id: 101, total: 249.0)
inv.prettyPrint()   // Output: Invoice #101 — Total: $249.00

Extending Protocols (Protocol Extensions)

Protocol extensions add default method implementations that every conforming type receives automatically.

protocol Greetable {
    var name: String { get }
}

extension Greetable {
    func greet() {
        print("Hello, I'm \(name)!")
    }
}

struct User: Greetable {
    var name: String
}

struct Bot: Greetable {
    var name: String
}

User(name: "Alice").greet()   // Output: Hello, I'm Alice!
Bot(name: "R2D2").greet()     // Output: Hello, I'm R2D2!

Neither User nor Bot implements greet() — they both get it free from the protocol extension.

Extension Restrictions

You CAN add via extensionYou CANNOT add via extension
Computed propertiesStored properties
Methods (including mutating)Property observers (didSet/willSet)
New initializersOverride existing methods
SubscriptsDesignated initializers on classes
Nested types
Protocol conformance

Summary

Extensions add computed properties, methods, initializers, and protocol conformances to any existing type — yours or Apple's — without touching the original code. Use them to keep large types organized, to add convenience helpers to built-in types, and to attach protocol conformance to types cleanly. Extensions are one of the most practical everyday tools in Swift.

Leave a Comment

Your email address will not be published. Required fields are marked *