Swift Extensions

Adding functionality to existing types

🔧 What are Swift Extensions?

Extensions add new functionality to existing classes, structures, enumerations, or protocols. You can extend types you don't own, including built-in Swift types, making your code more expressive and organized without modifying original implementations.


// Basic extension example
extension String {
    func isPalindrome() -> Bool {
        let cleaned = self.lowercased().replacingOccurrences(of: " ", with: "")
        return cleaned == String(cleaned.reversed())
    }
}

let text = "A man a plan a canal Panama"
print(text.isPalindrome()) // Output: true

let word = "hello"
print(word.isPalindrome()) // Output: false
                                    

Key Extension Features

⚙️

Add Methods

Add new instance and type methods

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

Computed Properties

Add computed properties to types

extension Double {
    var km: Double { return self * 1000.0 }
    var m: Double { return self }
    var cm: Double { return self / 100.0 }
}
🔧

Initializers

Add convenience initializers

extension CGRect {
    init(center: CGPoint, size: CGSize) {
        let origin = CGPoint(x: center.x - size.width/2, 
                           y: center.y - size.height/2)
        self.init(origin: origin, size: size)
    }
}
📋

Protocol Conformance

Make types conform to protocols

extension String: Identifiable {
    public var id: String { return self }
}

🔹 Extending Built-in Types

Add useful functionality to Swift's built-in types:

extension Array {
    func chunked(into size: Int) -> [[Element]] {
        return stride(from: 0, to: count, by: size).map {
            Array(self[$0..<Swift.min($0 + size, count)])
        }
    }
    
    var isNotEmpty: Bool {
        return !isEmpty
    }
}

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

let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9]
let chunks = numbers.chunked(into: 3)
print("Chunks: \(chunks)")

print("Is array not empty? \(numbers.isNotEmpty)")

let number = 4
print("\(number) is even: \(number.isEven)")

3.times {
    print("Hello!")
}

Output:

Chunks: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

Is array not empty? true

4 is even: true

Hello!
Hello!
Hello!

🔹 Extending Custom Types

Add functionality to your own types using extensions:

struct Person {
    let firstName: String
    let lastName: String
    let age: Int
}

extension Person {
    var fullName: String {
        return "\(firstName) \(lastName)"
    }
    
    var initials: String {
        return "\(firstName.first!)\(lastName.first!)"
    }
    
    func isAdult() -> Bool {
        return age >= 18
    }
    
    func greet() -> String {
        return "Hello, I'm \(fullName) and I'm \(age) years old."
    }
    
    static func createChild(firstName: String, lastName: String) -> Person {
        return Person(firstName: firstName, lastName: lastName, age: 0)
    }
}

let person = Person(firstName: "John", lastName: "Doe", age: 25)
print(person.fullName)
print("Initials: \(person.initials)")
print("Is adult: \(person.isAdult())")
print(person.greet())

let child = Person.createChild(firstName: "Jane", lastName: "Smith")
print("Child: \(child.fullName), Age: \(child.age)")

Output:

John Doe

Initials: JD

Is adult: true

Hello, I'm John Doe and I'm 25 years old.

Child: Jane Smith, Age: 0

🔹 Protocol Extensions

Provide default implementations for protocol methods:

protocol Describable {
    var description: String { get }
}

extension Describable {
    func printDescription() {
        print("Description: \(description)")
    }
    
    func detailedDescription() -> String {
        return "Detailed: \(description)"
    }
}

struct Book: Describable {
    let title: String
    let author: String
    
    var description: String {
        return "\(title) by \(author)"
    }
}

struct Movie: Describable {
    let title: String
    let director: String
    let year: Int
    
    var description: String {
        return "\(title) (\(year)) directed by \(director)"
    }
}

let book = Book(title: "1984", author: "George Orwell")
let movie = Movie(title: "Inception", director: "Christopher Nolan", year: 2010)

book.printDescription()
movie.printDescription()

print(book.detailedDescription())
print(movie.detailedDescription())

Output:

Description: 1984 by George Orwell

Description: Inception (2010) directed by Christopher Nolan

Detailed: 1984 by George Orwell

Detailed: Inception (2010) directed by Christopher Nolan

🔹 Conditional Extensions

Add functionality only when certain conditions are met:

extension Array where Element: Numeric {
    func sum() -> Element {
        return reduce(0, +)
    }
    
    func average() -> Double {
        guard !isEmpty else { return 0 }
        let total = sum()
        return Double(total as! Double) / Double(count)
    }
}

extension Array where Element == String {
    func joinedWithCommas() -> String {
        return joined(separator: ", ")
    }
    
    func longestString() -> String? {
        return self.max { $0.count < $1.count }
    }
}

let numbers = [1, 2, 3, 4, 5]
let doubles = [1.5, 2.5, 3.5, 4.5]
let names = ["Alice", "Bob", "Charlie", "David"]

print("Sum of numbers: \(numbers.sum())")
print("Average of doubles: \(doubles.average())")
print("Names: \(names.joinedWithCommas())")
print("Longest name: \(names.longestString() ?? "None")")

Output:

Sum of numbers: 15

Average of doubles: 3.0

Names: Alice, Bob, Charlie, David

Longest name: Charlie

🔹 Extension Best Practices

Follow these guidelines when creating extensions:

✅ Good Practices:

  • Organize by functionality: Group related methods in the same extension
  • Use meaningful names: Make extension methods clear and descriptive
  • Separate concerns: Use different extensions for different protocols or features
  • Document public extensions: Add comments for extensions others will use
  • Consider performance: Extensions should not significantly impact performance

⚠️ Limitations:

  • Cannot add stored properties (only computed properties)
  • Cannot override existing methods
  • Cannot add designated initializers to classes
  • Cannot make a class inherit from another class

🧠 Test Your Knowledge

What can you NOT add to a type using extensions?