Lesson 75Protocols

Extensions

What are Extensions?

Extensions add new functionality to existing types - even types you don't own like Int, String, or Array. Incredibly powerful for organizing code!

What Extensions Can Add

Computed Properties

Add new computed properties

var squared: Int { ... }

Methods

Add instance and type methods

func doSomething() { }

Initializers

Add convenience initializers

init(custom: Type) { }

Nested Types

Define new nested types

enum Kind { ... }

Constrained Extensions

extension Array where Element: Equatable {
    // Only available when Element is Equatable
}

Limitations

  • Cannot add stored properties
  • Cannot override existing functionality
  • Cannot add designated initializers to classes
  • Cannot add property observers to existing properties

Best Practice: Code Organization

Use extensions to group related functionality:

  • Protocol conformance in separate extensions
  • UI setup vs data handling
  • Private helpers in private extensions
main.swift
// EXTENSIONS
// Add new functionality to existing types

// EXTENDING BUILT-IN TYPES
extension Int {
    var squared: Int {
        return self * self
    }

    var isEven: Bool {
        return self % 2 == 0
    }

    func times(_ action: () -> Void) {
        for _ in 0..<self {
            action()
        }
    }
}

print(5.squared)    // 25
print(4.isEven)     // true
3.times { print("Hello!") }  // Prints 3 times

// EXTENDING STRING
extension String {
    var reversed: String {
        return String(self.reversed())
    }

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

    func truncated(to length: Int) -> String {
        if self.count <= length {
            return self
        }
        return String(self.prefix(length)) + "..."
    }
}

print("Hello".reversed)  // olleH
print("Hello World Swift".wordCount)  // 3
print("Hello World".truncated(to: 5))  // Hello...

// EXTENDING ARRAY
extension Array where Element: Numeric {
    var sum: Element {
        return reduce(0, +)
    }
}

print([1, 2, 3, 4, 5].sum)  // 15

// ADDING INITIALIZERS
extension Date {
    init(year: Int, month: Int, day: Int) {
        var components = DateComponents()
        components.year = year
        components.month = month
        components.day = day
        self = Calendar.current.date(from: components) ?? Date()
    }
}

let birthday = Date(year: 2000, month: 6, day: 15)

// ADDING METHODS TO YOUR OWN TYPES
struct Point {
    var x: Double
    var y: Double
}

extension Point {
    var magnitude: Double {
        return (x * x + y * y).squareRoot()
    }

    func distance(to other: Point) -> Double {
        let dx = other.x - x
        let dy = other.y - y
        return (dx * dx + dy * dy).squareRoot()
    }

    mutating func translate(dx: Double, dy: Double) {
        x += dx
        y += dy
    }
}

// NESTED TYPES IN EXTENSIONS
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(5.kind)   // positive
print((-3).kind) // negative

// ORGANIZING CODE WITH EXTENSIONS
class ViewController {
    var title: String = ""
}

// Group related functionality
extension ViewController {
    func setupUI() {
        print("Setting up UI")
    }

    func configureNavigation() {
        print("Configuring navigation")
    }
}

extension ViewController {
    func loadData() {
        print("Loading data")
    }

    func saveData() {
        print("Saving data")
    }
}

// CONSTRAINED EXTENSIONS
extension Array where Element: Equatable {
    func removingDuplicates() -> [Element] {
        var result: [Element] = []
        for element in self {
            if !result.contains(element) {
                result.append(element)
            }
        }
        return result
    }
}

let nums = [1, 2, 2, 3, 3, 3]
print(nums.removingDuplicates())  // [1, 2, 3]

// EXTENSION LIMITATIONS
// Cannot add stored properties
// Cannot override existing functionality
// Cannot add designated initializers to classes

Try It Yourself!

Extend Double with properties for celsius and fahrenheit conversion, and a rounded(toPlaces:) method!