Protocols in Swift

Protocols are extremely important in Swift

A protocol allows objects, and provide a concrete implementation of any object that conforms to it. We can think of a protocol as a set of rules and guidelines that an object can conform to

For the examples, read on.

Image for post
Image for post
Photo by Mike Meyers on Unsplash

Difficulty: Beginner | Easy | Normal | Challenging

Prerequisites:

  • Be able to produce a “Hello, World!” iOS application (guide HERE)
  • Use of extensions (guide HERE)
  • Some knowledge of Object-oriented programming, specifically overriding (guide HERE)

Terminology

class: An object defined in Swift, using pass by reference semantics

conforms: conformance is if an object adopts a protocol (or inherits it from another class) and contains the functionality specified

enum: A type consisting of a set of named values, called members

extensions: Extensions add new functionality to a class, struct, enum or protocol

inheritance: The mechanism in which one class acquires the property of another class

method: A group of statements that together can perform a function

protocol: A blueprint on methods, properties and requirements to suit a piece of functionality

property: An association of a value with a class, structure or enumeration

struct: An object defined in Swift, using pass by value semantics

The origin of protocols

In natural English a protocol is a say that two parties agree to communicate. A simple example of this is a traditional handshake in England (an alternative is the bow in Japan).

In computing this idea has often been adopted in communications, for example the sending of acknowledgements in the Internet Protocol.

Image for post
Image for post
The Transmission Control Protocol

The meaning of protocol in Swift is tightly coupled to this meaning, although it is subtly different as we will see.

In this example we will make a socialized person, represented by the following struct:

struct Person {
var name: String
}
let anne = Person(name: "Anne")

A comparison of protocols and extensions

A protocol means you can specify the properties and methods that something must contain, and then treat the object as the protocol.

We are able to use extensions to provide methods that don’t exist on a current class, struct or enum; one advantage being that you don’t even need to own the existing object to add functionality using this technique.

A protocol is adopted by a class, struct or enum to define the requirements of the object such that a concrete implementation of the protocol is provided.

The syntax

A simple implementation

We can set up a protocol that simply demands any object conforms that to demonstrate the syntax:

protocol GreetingProtocol {
func sayHello()
}

Note that the function does no have a body. The reason for this is that this is a protocol so we can think of this like a prototype that does not have a concrete implementation of the function.

We can then update our person to conform to our GreetingProtocol

struct Person: GreetingProtocol {
func sayHello() {
print ("Hello")
}
var name: String
}
let anne = Person(name: "Anne")

To be noted here is the function signature where we declare that Person will conform to the GreetingProtocol with the following struct : Person GreetingProtocol which demands that we include a function func sayHello() which indeed it does in the implementation above.

Image for post
Image for post

Adding properties

The protocol above specified a method that needs to be included in objects that conform to the GreetingProtocol. We can also demand that any person that conforms to the GreetingProtocol has a name by updating the protocol to the following:

protocol GreetingProtocol {
func sayHello()
var name: String { get set }
}

where get indicates that the string can be read, and set indicates that it can be written.

Playing with Protocols

Treating Protocols like a type

The examples used follow on from the struct written above.

We can create an array of our protocol type

// an array of Person
let arr = [anne]
type(of: arr) // [Person]
// an array of GreetingProtocol
let arrProtocol: [GreetingProtocol] = [anne]
type(of: arrProtocol) // [GreetingProtocol]

Protocol inheritance

A protocol can inherit from another protocol, which is known as protocol inheritance. It is possible to inherit from one or more protocols and still add more protocols to a specific class, struct or enum.

We can set up three different behaviours for a football player (that is, a decent player and not one who plays for Liverpool):

protocol Strikes {
func finish() -> Bool
}
protocol Tackles {
func slides() -> Bool
func blocks() -> Bool
}
protocol Moves {
func runs()
func jogs()
func walks()
}

We then create a single protocol that collects together all the Protocols in a single Protocol in

protocol Player: Strikes, Tackles, Moves {}

The end curly brackets {} indicates that the Player Protocols doesn’t need to have any implementation to conform to the Protocols that it inherits from. This makes sense in that Protocols do not have implementations!

If we want a concrete Player that conforms to the Player Protocol this is no real problem. An example? Here it is:

class Teemu: Player {
func finish() -> Bool {
return true
}
func slides() -> Bool {
return true
}
func blocks() -> Bool {
return true
}
func runs() {
print ("runs")
}
func jogs() {
print ("jogs")
}
func walks() {
print ("walks")
}
// give a name
var name: String = "Pukki"
}

which can then be instantiated

var striker = Teemu()

and then we can call the functions

striker.walks()

Conditionally conforming to a protocol

It is possible to conditionally conform to a Protocol in Swift, by adding constraints.

These constraints are much like (please understand; exactly the same) as the generic where clauses described HERE, but extend a Protocol rather than a Class).

If we want to extend the Teemu class above (to give him the ability to save) it would look something like the following:

extension Player {
func save() -> Bool {
return false
}
}

and then we can access this function from our striker instantiation as a default implementation of the save() function is now avaliable to every class that conforms to the Player Protocol.

striker.save()

Limitations

Unfortunately you can’t use default arguments in a protocol method.

So if we add a “SayHello” function to our moves protocol

protocol Moves {
func runs()
func jogs()
func walks()
func sayHello(name: String = "Dave) // ERROR
}

The exact error being “Default argument not permitted in a protocol method

Conclusion…

protocols can’t be created themselves, and can be thought of as a description (or prototype) rather than a type itself. You can code using a Protocol-Oriented Approach in Swift, which makes it well worth learning!

Want to get in contact? Try the link here:

https://twitter.com/stevenpcurtis

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store