Protocols

16 Aug 2022

The evolution of structs into class-like things that can hold properties and methods in Swift raised in my mind “what about inheritance?” - but no: structs in Swift can not use inheritance.

Swift classes implement inheritance, but only from one class; there’s no multiple inheritance. Protocols neatly address both these concerns to a large extent, but perhaps before we look at how they work, we should have a brief diversion into inheritance in C++.

#include

class Shape { public: int sides; };

class Drawable { public: virtual void draw() {} };

class Square : public Shape, public Drawable { public: Square(){ sides = 4; }

    void draw() {
        std::cout << "■" << std::endl;
    }

};

int main() { Square square; square.draw(); return 0; }

In the code above, there’s two classes Shape and Drawable. You could regard Drawable as an interface if you’re coming from Java world (because the method draw() is marked virtual - there’s no implementation). The class Square inherits from both those classes - it’s a new class that is a Shape, but is also Drawable. (Line 15 above)

Perhaps in this program there’s other items such as images or text - ie not shapes which might also inherit from Drawable. Elsewhere, we could have a method that had to draw a collection of things - it doesn’t care what the items are, as long as they are Drawable - they have to implement the Draw() method.

Swift addresses most of these needs with Protocols. A protocol defines a set of properties and methods. Then a struct, class, or even enum can conform with this protocol - they contain the same properties, and are required by the compiler to implement the methods from the protocol.

Here’s the Swift equivalent of the C++ above:

protocol Shape { var sides: Int { get set } }

protocol Drawable { func draw() }

struct Square: Shape, Drawable { var sides: Int = 4 func draw() { print("■") } }

let square = Square() square.draw()

So, that’s cool, but not amazing. The power is really that now we can write a function that takes anything that’s Drawable as if it was a real type. To wit:

class IanClass: Drawable { func draw() { print(“Ian”) } }

func drawAThing(_ thingToDraw: Drawable){ thingToDraw.draw() }

let squareStruct = Square() let ianClass = IanClass()

drawAThing(squareStruct) drawAThing(ianClass)

So the function drawAThing() is happy to draw anything, as long as it conforms with the Drawable protocol by implementing the draw() method. It doesn’t even matter what it is - as in this example where we’ve passed it a struct on one occasion, and a class on another.

Protocols are a great example of Swift being flexible while being type-safe.