Swift Programming – Extensions

In this tutorial you’ll learn about extensions in Swift. To follow along, it is recommended to have a Mac and the Xcode IDE installed on it. This tutorial makes use of the Xcode playground for compilation of the Swift codes. If you do not have a Mac, you may try using the open-source code editors which support different operating systems such as AtomSublimeText or VSCode.

Extensions

You can add new functionality to an existing class, structure, enumeration or protocol type by using extensions. Extensions support computed instance and type properties, instance and type methods, new initializers, subscripts, nested types and conform to a protocol. You define an extension with the extension keyword followed by the existing type’s name that you want to extend and curly braces { }.

Let’s try an example to better understand this concept. Let’s say we want to extend the type Double for kg and pound conversions.

extension Double {
    var kg: Double {
        return self * 2.20462
    }
    
    var pound: Double {
        return self / 2.20462
    }
}

let convertKgToPound = 65.kg
print("65 kg = \(convertKgToPound) pound")

let convertPoundToKg = 99.208.pound
print("99.208 pound = \(convertPoundToKg) kg")

In our example above, we defined an extension of the type Double and added two computed properties kg and pound. Inside the computed properties we are returning the result of a calculation based on the value provided (denoted by the self keyword). This means that when we declared the constant convertKgToPound, we are getting the value 65 via the self keyword. We appended a dot (.) with the appropriate computed properties names, .kg and .pound.

Initializers

You can add convenience initializers in extensions. However, you are not allowed to add new designated initializers or deinitializers to a class.

struct Size {
    var base: Double = 0.0
    var height: Double = 0.0
}

struct Shape {
    var triangle = Size()
    var parallelogram = Size()
}

extension Shape {
    init(base: Double, area: Double, isTriangle: Bool) {
        var height: Double = area / base
        
        if isTriangle {
            height = height / 0.5
        }
        
        let size = Size(base: base, height: height)
        
        self.init(triangle: isTriangle ? size : Size(), parallelogram: isTriangle ? Size() : size)
    }
    
    var areaOfTriangle: Double {
        return 0.5 * triangle.base * triangle.height
    }
    
    var areaOfParallelogram: Double {
        return parallelogram.base * parallelogram.height
    }
}

var shape = Shape(triangle: Size(base: 14, height: 10))
print("Area of the triangle is \(shape.areaOfTriangle)")

shape = Shape(base: 5, area: 15, isTriangle: false)
print("Height of the parallelogram is \(shape.parallelogram.height)")

In the example above we defined 2 structures Size and Shape. We defined an extension of Shape with a convenience initializer taking 2 parameters of types Double and 1 parameter of type Bool. We also defined two computed properties in our Shape extension.

Methods

You can define instance and type methods in the extension of an existing type.

extension Int {
    func average() -> Double {
        var sum = 0
        var count = 0
        
        for i in 1...self {
            count += 1
            sum += i
        }
        
        return Double(sum)/Double(count)
    }
    
    mutating func factorial() {
        let n = self
        
        var k = 1
        while k < (n - 1) {
            self = self * (n - k)
            k += 1
        }
    }
}

print(10.average())

var five = 5
five.factorial()
print("5! = \(five)")

In the example above, we extended the type Int with instance method average() and mutating instance method factorial().

Subscripts

You can define subscripts in the extension of an existing type.

extension String {
    subscript(index: Int) -> Character {
        var character: Character = self.first ?? "A"
        var countIndex = 0
        
        for i in self.indices {
            if countIndex == index {
                character = self[i]
                break
            }
            
            countIndex += 1
        }
        
        return character
    }
}

let text = "Hello World!"
print(text[6])

In the example above, we extended the type String with a subscript. The subscript will return the Character at the specific index we provide.

Reference

https://docs.swift.org/swift-book/LanguageGuide/Extensions.html