NEW: Check out my incredible new SpriteKit book! >>

< Previous: Creating a Swift extension   Next: Extensions for brevity >

Protocol-oriented programming for beginners

One of Swift’s most powerful features is its ability to extend whole swathes of data types at the same time. This is a pretty advanced topic known as protocol-oriented programming, but I want to at least give you a taster of what’s possible – if you’re interested in learning more, you should check out my Pro Swift book.

To demonstrate how this works, let’s look at another simple extension for the Int data type: we’re going to add a squared() method that multiples an integer by itself.

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

let i: Int = 8
print(i.squared())

I explicitly made i an Int for a reason: there are other kinds of integers available in Swift. For example, UInt is an unsigned integer, which means it sacrifices the ability to hold negative numbers in exchange for the ability to hold much larger positive numbers.

There are also integers of different sizes, e.g. Int8 holds an integer made up of 8 binary digits, which holds a maximum value of 127, and UInt64 is the largest type of integer and holds up to 18,446,744,073,709,551,615 – that’s 18 quintillion four hundred and forty-six quadrillion in case you were wondering.

Our extension modifies the Int data type specifically, rather than all variations of integers, which means code like this won’t work because UInt64 doesn’t have the extension:

let j: UInt64 = 8
print(j.squared())

Swift’s solution is to let us create protocol extensions: extensions that modify several data types at once.

You’ve already seen how the self keyword lets us refer to our current value, so self * self means “multiply my current number by itself.” Well, there’s also Self with a capital S, which has a subtly different meaning: it means “my current data type.” So, self means “my current value” and Self means “my current data type.”

This matters when it comes to extending protocols because of the way our squared() method is declared. Take a look again:

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

If we want squared() to apply to all types of integer, we can’t very well make it return Int - that’s not big enough to hold the full range of a UInt64, so Swift will refuse to build. Instead, we need to make the method return Self, which means “I’ll return whatever data type I was used with.”

Here’s the rewritten extension:

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

This time I’ve made it apply to BinaryInteger, which is the protocol applied to Int, Int8, UInt64, and so on. This means all integer types get access to the squared() method, and work as expected.

Learn Server-Side Swift now!

Take your Swift code to the server and become a full-stack developer with my latest book: Server-Side Swift!

< Previous: Creating a Swift extension   Next: Extensions for brevity >
Click here to visit the Hacking with Swift store >>