Day 11 - 100 Days of Swift

5 minute read

Protocols and Extensions

Day 11 is about protocols and extensions. You look at how to define a protocol, and how one protocol can inherit from others. You look at adding functionality to a type with extensions, and adding default implementations to protocols with them. And you briefly look at the concept of “protocol-oriented programming” where you craft your code around protocols and protocol extensions.

Protocols are basically just descriptions of what properties and methods something has to have. You define them in pretty much the same way as you define a struct or a class, with a few small differences:

1
2
3
4
5
6
7
8
9
10
11
protocol Badged {
    // Properties must be marked as 'get' or 'get set'
    var badgeID: String { get set }
}

protocol PaperworkFiling {
    // Methods are defined, with parameters and return
    // types, but they aren't implemented
    func filePaperwork(formNumber: Int,
                       content: String) -> Bool
}

You adopt them in the same way that you inherit from a class, except that structs can also adopt protocols, and the same type can adopt multiple protocols:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class ParksEmployee: Badged, PaperworkFiling {
    var name: String = "Annonymous"
    var birthdate = Date()
    var badgeID: String = "1234"

    func filePaperwork(formNumber: Int,
                       content: String) -> Bool {
        // Do some work...
        return true
    }
}

let jerry = ParksEmployee()
jerry.name = "Jerry Gergich"
jerry.birthdate = Date(timeIntervalSince1970: -689187600)

let content = "Need a permit for a bird"
let didPaperwork = jerry.filePaperwork(formNumber: 12,
                                       content: content)
print(didPaperwork)
// true

Protocols can even inherit from other protocols. This allows you to combine several disparate protocols into one:

1
protocol BadgedAndPaperwork: Badged, PaperworkFiling { }

An extension is exactly what it sounds like. They let you add functionality to existing types, including the types that are built into the standard library:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
extension ParksEmployee {
    func clockOut() {
        print("See you guys tomorrow!")
    }
}

jerry.clockOut()
// "See you guys tomorrow!"

import Foundation
extension Int {
    func isPrime() -> Bool {
        guard self > 1 else { return false }
        guard self > 3 else { return true }
        let limit = Int(sqrt(Double(self))) + 1
        for i in 2...limit {
            if self % i == 0 { return false }
        }
        return true
    }
}

print(5.isPrime())      // true
print(8.isPrime())      // false
print(120003.isPrime()) // false

You can’t put stored properties in extensions, but you can put computed properties:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
extension ParksEmployee {
    // You can't do this, won't compile.
    var nickNames: [String] = []

    // You can do this though
    var approxAge: Int {
        // This is a bad way to calculate age
        // don't do this.
        let interval = Date().timeIntervalSinceNow -
                       birthdate.timeIntervalSinceNow
        return Int(interval) / 60 / 60 / 24 / 365
    }
}

print(jerry.approxAge) // 71

You can extend protocols as well. This lets you add default functionality for some or all of the protocol’s methods:

1
2
3
4
5
6
7
8
9
extension PaperworkFiling {
    func filePaperwork(formNumber: Int,
                       content: String) -> Bool {
        print("Filing form number \(formNumber)")
        print("Content: \(content)")
        // Do some work...
        return true
    }
}

When you put all this together, you can get into a design pattern called “Protocol-Oriented Programming” (sometimes abbreviated as POP). POP lets you break down large and complex inheritance structures into smaller protocols that are easier to follow and simpler to modify:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// The previous extension means that we don't have
// to supply the filePaperwork functionality for
// new classes that adopt our protocol.
class CityPlanner: PaperworkFiling {
    var name: String

    init(name: String) {
        self.name = name
    }
}

let mark = CityPlanner(name: "Mark Brendanawicz")

// We get this for free
mark.filePaperwork(formNumber: 12,
                   content: "Still need that bird permit")
// "Filing form number 12"
// "Content: Still need that bird permit"

Reflections

The concept of protocol-oriented programming is really intriguing to me. I don’t really know anything about it, and I don’t feel like he went into too much detail about it today. But it definitely seems like it could be really useful. Something worth learning. Maybe after I finish up the 100 days of Swift, I will see if I can find someone who’s putting out good material about it. And even if I don’t design all of my code around protocols, I’m sure I can find some places where a well defined protocol with some default functionality could simplify things or add clarity.