Protocols in Swift: Blueprints for success

Learning Swift


This article is part of the Learning Swift series.

The next three articles are going to talk about protocols, closures, and extensions. I’ve decided to break these out into individual articles because they are new for a developer unfamiliar with C-type languages, and could use a little more depth in the discussion.

In this article, I’m going to talk about protocols. Protocols act as a blueprint for classes, enumerations, and structures. They allow you to define, at a high level, the properties and functions that should exist in any data structure created from them, and disallow any deviation.

For example, you might want to create a protocol for animals:

protocol Animal {

var legs: Int { get set }
var domesticated: Bool { get }

func hasFur() -> String

func countLegs() -> String

}

This protocol says that if you are going to create an object using the Animal protocol, it should define two variables, legs and domesticated, and two functions, hasFur() and countLegs(). When you go to create an object based on the protocol, it should look like this:

class Dog: Animal {
var legs = 4
let domesticated = true

func hasFur() -> String {
return “The dog is furry”
}

func countLegs() -> String {
return “The dog has \(legs) legs”
}

}

If I forget to define one of the properties or functions when creating a new class based on the Animal protocol, or I attempt to add a property or new function, Xcode will report an error.

For example, if I write the following class definition based on the Animal protocol above, Xcode will report an error with the name variable:

class Dog: Animal {
var legs = 4
var domesticated = true
var name = “”

func hasFur() -> String {
return “The dog is furry”
}

func countLegs() -> String {
return “\(name) the dog has \(legs) legs”
}

}

let Fido = Animal()
Fido.name = “Ralph”
Fido.legs = 3
Fido.countLegs() // Prints “Ralph the dog has 3 legs”

println(Fido.domesticated) // Prints “true”

Protocols, like classes, can inherit properties and functions from other protocols. For example, I might want to create protocols for different classes of animals, like pets, livestock, or working animals. It’s a waste of time to redefine properties and functions I know all of them will share, like the legs or domesticated property. So it makes sense to define an Animal protocol, and have subsequent protocols inherit from it. In code, this looks something like the following:

protocol Animal {

var legs: Int { get set }
var domesticated: Bool { get }

func hasFur() -> String
func countLegs() -> String

}

protocol Pet : Animal {
var leashed: Bool { get set }
var pettable: Bool { get set }
var foodType: String { get set }
var name: String { get set }

}

class Dog: Pet {
var legs = 4
let domesticated = true
var leashed = true
var pettable = true
var foodType = “Kibble”
var name = “”

func hasFur() -> String {
return “The dog is furry”
}

func countLegs() -> String {
return “\(name) the dog has \(legs) legs”
}
}

let Fido = Dog()
Fido.name = “Ralph”
Fido.legs = 3
Fido.countLegs() // Prints “Ralph the dog has 3 legs”

println(Fido.foodType) // Prints “Kibble”

As you can see, I still have to implement every property and method when I create an object based on the Pet protocol, even those properties and methods defined in the Animal protocol. However, the implementation of a class based on the Pet protocol might be somewhat different from one based on the Livestock protocol (though they will share those properties defined in the Animal protocol).

Some things to know about protocols

There are some special notes that must be kept in mind when dealing with protocols.

First, is how protocols deal with properties. When you are defining a protocol, any properties must be defined using the var keyword, despite how you plan on implementing them later. For example, in the Animal property above, you’ll notice that the property is defined by using the var keyword. If you attempt to use the let keyword in a protocol, Xcode will report an error. You will also note that every property definition has either { get } or { get set } after it. This is to define whether the protocol can be used in a variable context or not.

If you define the property with { get } only, then the class, enumeration, or structure that uses the protocol can define how it wants to implement the property — as either a constant or a variable. You can see that in the Dog class — we have implemented ‘domesticated’ as a constant, rather than a variable, to ensure the value of that property in that context cannot be changed (as dogs are always domesticated).

If you define the property with { get set }, then this can only be implemented as a variable. If you attempt to use the let keyword when you create a class, structure, or enumeration with the protocol, Xcode will report an error.

If you’re unsure how you want to implement a property, then only use the { get } definition when adding your property to a protocol. This will give you the flexibility to select when you use the protocol.

Method definitions in protocols are very simple. In most cases, it’s a matter of writing func myMethodName() -> return_type and calling it a day; this is always the case when using protocols to define a blueprint for classes. There are times, however, when you might need to create a structure or enumeration that modifies properties within the instance created using the protocol. The Swift Programming Language provides a good example of this:

protocol Togglable {
mutating func toggle()
}

enum OnOffSwitch: Togglable {
case Off, On
mutating func toggle() {
switch self {
case Off:
self = On
case On:
self = Off
}
}
}

Let’s extend this to show how the protocol can be used to define a different kind of toggling:

protocol Togglable {
mutating func toggle()

}

enum OnOffSwitch: Togglable {
case Off, On
mutating func toggle() {
switch self {
case Off:
self = On
case On:
self = Off
}
}
}

enum VolumeButton: Togglable {
case Low, Medium, High, Off
mutating func toggle() {
switch self {
case Off:
self = Low
case Low:
self = Medium
case Medium:
self = High
case High:
self = Off
}
}
}

As you can see, this still satisfies the blueprint set out in the Togglable protocol — we have a function called toggle that sets a value based on the current state.

Why use protocols?

Protocols are useful for a couple of reasons — first, they act as a safety check for your own coding. If you know you are going to be creating multiple classes, enumerations, or structures based on a standard design, protocols help enforce that design. If you create a class based on a protocol that doesn’t contain all of the properties and methods defined, Xcode will immediately tell you that you are not conforming to the design.

Protocols are also useful if you are creating or exposing an API for your code to other applications. This ensures that other developers building code using your API in a safe and responsible manner. Of course, you’ll have to document your protocols, as Xcode does not necessarily inform you as to what is missing in terms of protocol compliance.

As always, if you have any comments, questions, or corrections, please drop me a line on Twitter at @staticred. Happy coding!

Did you enjoy this article? Purchase it in iBooks.

Image credit: Blueprint by Flickr user Will Scullin. Used under Creative Commons.