top of page

Mastering Swift's Development Blog

Follow Us On Twitter
  • Writer's pictureJon Hoffman

Swift Protocols Part 1: Understanding the Protocol

Updated: Sep 4, 2022


Protocols are the core in which the Swift programming language is built upon. Understanding how they work and what makes them so powerful is essential to understanding the language itself. The protocol is so important that we have created this four-part series on it. We plan on releasing the other three parts over the next two weeks.


In this first post we will give a brief introduction to what protocols are and how to use them. In Swift Protocols Part 2, we will dive deep into protocol inheritance and composition, while really looking at how and when to use each of them. In Swift Protocols Part 3, we will examine protocol extensions and how to effectively use them. Finally in Swift Protocols Part 4, we will scrutinize associated types with protocols and see what makes them so powerful.


What is the Protocol

Swift’s protocols enable us to define the functionality for types that adopts them. A protocol can be thought of as the blueprint which defines the methods, properties, and other functionality for types. We can optionally, with protocol extensions, provide default implementations and new functionality for types that adopt protocols.


In the infamous “Crusty Talk” at WWDC 2015, Dave Abrahams famously said “Don’t start with the class. Start with the Protocol”. I would not say that is totally the right answer but it is good to think about the protocol and understand where it fits into your design. Dave Abrahams later clarified that statement by saying “Use value types, then if you need polymorphism, make them conform to a protocol”. That clarification does ring truer to me and is really the foundation of Protocol Oriented Design. Let’s start off with the basics and look at the syntax for the protocol.


The Protocol’s Syntax

We define a protocol just like we do a struct, class or enum in swift. We start off with the protocol keyword followed by the name of the protocol. The following illustrates how to do this.

protocol MyProtocol {
    
}

An empty protocol really isn’t that useful, so let’s look at how we can add property and function definitions to a protocol by creating a simple protocol that can be used to define a full name.

protocol FullName {
    var firstName: String { get set }
    var lastName: String { get set }
    
    func getName() -> String
}

In the FullName protocol we are defining two properties named firstName and lastname. These properties are of the String type and are defined as read-write properties (also known as variables) because we define the property with both get and set. If we wanted a read only property (also known as constants) we would only use get.


We then defined one function named getName() which has a return value of the String type. Notice that we do not put any functionality in the function, only the function definition.


If a structure has a function that will be changing the value of a property, we need to use the mutating keyword when defining the function. With this in mind, if a function defined by the protocol will be changing one of the properties, and a structure may adopt this protocol, then we need to also use the mutating keyword to define the function within the protocol. The following example shows this

protocol MyProtocol {
    mutating func myMutatingFunction()
}

Now that we have seen the basics of how to define a protocol, let’s look at how we would adopt a protocol.


Adopting a Protocol

A protocol in Swift can be adopted by a struct, class or enum but the type that adopts it must implement all requirements defined within the protocol. To adopt a protocol, we add the name of the protocol to the type’s definition as shown in this example

struct BasketballPlayer: FullName {

}

This code says that the structure BasketballPlayer is adopting the FullName protocol. This means that it is promising that it will implement all requirements defined within the FullName protocol. The previous code will give us an error saying that type BasketballPlayer does not confirm to the FullName protocol because it does not implement the requirements. We can fix this error by adding the properties and functions defined by the protocol as show in the next example

struct BasketballPlayer: FullName {
    var firstName: String
    var lastName: String
    
    func getName() -> String {
        return firstName + " " + lastName
    }
}

NOTE: We can define optional functions by using the @objc attribute but then only classes can adopt the protocol. I would recommend not using the @objc attribute unless it is necessary. We could also make optional properties and functions by provide default implementations with protocol extensions, but with default implementations provided by the extension, the functions and properties are still there.


Now let’s look at how protocols are treated as full-fledged types in Swift


Protocols as Types

While no functionality is implemented within a protocol, in the Swift language, they are still treated as full-fledged types and can be used like any other types. This means that they can be used as parameter and/or return types for functions as well as the types for variables, constants, and collections. For the examples in this section, we will be using the FullName protocol that we defined in the previous section.


After defining the FullName protocol, we can now use it anywhere a normal type would be used. Here is an example type that shows how we can use the FullName protocol as a function parameter and return type as well as the type for a property.

struct MyType {
    var person: FullName
    
    func getName() -> String {
        return person.getName()
    }
    mutating func setPerson(_ person: FullName) {
        self.person = person
    }
}

When we use a protocol as shown here, any instance of a type that adopts the FullName protocol can be used wherever the protocol is used. What this means is if we have two types, BasketballPlayer and SwiftProgrammer, that both adopt the FullName protocol, we could use instances of either for the person property and the parameter for the setPerson()function. This is a form of polymorphism where we have a single interface for many types. The FullName protocol defines the interface that is used to interact with the instance. The drawback to this is, unless we typecast the instance, we can only use the functionality defined by the FullName protocol when interacting with the type. Any other functionality specific to the concrete type would not be available.


If we needed to access specific functionality of the concrete type, we can typecast the type. Let’s see how this works. In this example we are assuming we have two concrete types, BasketballPlayer and SwiftProgrammer that both adopt the FullName protocol.

var people = [FullName]()
//add people to the array

for person in people {
    switch person {
    case is BasketballPlayer:
        print("Basketball Player")
    case is SwiftProgrammer:
           print(“Swift Programmer”)
    default:
        print("Unknown")
    }
}

Using the switch statement to typecast is probably preferrable because it is easier to catch each type without a lot of if/else statements but if you only need to check one or two cases you could also typecast with the if statement like this:

if player is BasketballPlayer {
    print("Basketball Player ")
}

As we can see in this first post, protocols are very easy to define and use. What we have not seen yet is what makes then extremely useful and powerful. In the next three parts of the protocol series of posts, we will show some of the more advance features of the protocol and will begin to demonstrate how powerful they are and why you should be using them. Here are the other posts in this series:


masteringSwift.jpeg

Mastering Swift 5.3

The sixth edition of this bestselling book, updated to cover through version 5.3 of the Swift programming language

Amazon

Packt

pop.jpeg

Protocol Oriented Programming

Embrace the Protocol-Oriented design paradigm, for better code maintainability and increased performance, with Swift.

Amazon

Packt

bottom of page