Better Programming

Advice for programmers.

Follow publication

How to Master Protocols in Swift

Piero Sifuentes
Better Programming
Published in
11 min readApr 6, 2020

Photo by Sven Mieke on Unsplash

Protocols represent interfaces like many other languages do. As you may already know, protocols are used to define a “blueprint of methods, properties, and other requirements that suit a particular task or piece of functionality.”

In this article, we’re going to talk in-depth about protocols using Swift 5.3. Let’s get started on a protocol basis.

Conforming a Protocol

Protocols allow you to group similar methods, functions, and properties. Swift lets you specify these interface guarantees on class, struct, and enum types. Only classtypes can use base classes and inheritance from a protocol.

Conforming a protocol in value and reference types

Protocol Properties and Functions

When defining a property within a protocol, we must specify its type, because the conforming types cannot inference the property type. In the same way, we cannot specify if the property will be a stored or computed property, leaving that specification to the conforming type.

We also must specify whether the property is a read-only or a read-write property by using the get and set keywords.

The default values to properties and method’s parameters are not allowed within a protocol.

When a value type conforms to our protocol, and this protocol has a function that modifies the instance it belongs to, we must prefix the function definition (within the protocol) with the mutating keyword.

If a reference type (class) conforms to that function with the mutating keyword, we don’t need to write the mutating keyword once we implement that func in our class.

Protocol properties and functions

Let’s make an example:

Protocol conformation variations

As we can see, a real read-only protocol should be conformed as a constant (let) or as a computed property.

Protocols also can define optional properties and functions but it requires to mark the protocol and the property and/or function with the objc attribute.

Optional protocol

Structures and enumerations cannot adopt protocols marked with the objc attribute. Only classes can adopt these protocols. Now, let’s take a look at the best protocol features.

Protocol Inheritance

In Swift, protocols can inherit from one or more additional protocols. Let’s see how and why to use it.

a user protocol

Here we have a User protocol, this protocol will be getting bigger each time we add a new User requirement into it. How do we solve this issue? Lets’s use protocol inheritance to make it a little better.

a user protocol using inheritance

Then, once we create a type that conforms to the User protocol, we must implement the requirements defined in the User protocol, as well as the requirements defined in the UserIdentity protocol.

a struct that conforms from User Protocol

This is a good way to start designing our code, now let’s see other great features of protocols.

Protocol Composition

Now we know how to use protocol inheritance, let’s see the next step.

The main weakness with reference types and OOP paradigm is that classes can only inherit from one superclass. Instead, with protocols and POP, you can use protocol composition, a feature that lets our types adopt multiple protocols.

Let’s see an example:

A common example using an object and delegate in a cell

Now, let’s make a few changes in our struct, it’s time to use protocols:

the same example using protocol composition

We can see a few benefits of this approach. First, we have a LocationCoordinate protocol to use whenever we need it. For instance, if we extend our app from restaurants to supermarkets, this new type can inherit from LocationCoordinate to get the coordinate properties.

Let’s see one great benefit. For example, see and imagine what the getDirections func will be like. We are sharing a restaurant to probably just summon am API like maps or Google Maps.

We’ve been sharing the whole object (that means the object with all the properties) for this feature, we may think, do we really need all these properties? If the answer is no, check out this solution:

Segregation using protocols

We’ve changed our get directions func for our RestaurantCellDelegate, so now we will only share the Restaurant properties and funcs that the getDirections feature requires to display the directions to go to that restaurant.

Now, I’ll describe one last great benefit in one example. Let’s imagine that we implement our RestaurantCellDelegate in a new ViewController, where this ViewController just has to display and handle everything related to reservations.

a view controller conforming RestaurantCellDelegate

We won’t implement showMenu, getDirections, and markFavorite funcs from our Delegate.

We have two options here, use the @objc and the optional keyword in our RestaurantCellDelegate or use protocol inheritance and composition.

As we already know, structures and enumerations cannot adopt protocols marked with the objc attribute, so we just have one option left.

a protocol composition to avoid the @objc optional

Now our ViewController will look like this:

A ViewController conforming to a protocol without unused funcs

As we’ve seen, protocol composition allows us to break our requirements into many smaller components rather than inheriting all requirements from a single protocol or single superclass.

This allows our type families to grow in width rather than height, which means we avoid creating bloated types that contain requirements that are not needed by all conforming types.

Always evaluate the use of protocols with one or two requirements in them, this will lead to handling a design that is hard to maintain and manage.

But this is not a rule. For some cases, delegates, for instance, we just need protocols with one or two funcs, evaluate before writing your code.

Protocol as Type

Events where protocols are just blueprints and don’t implement anything, they are types in Swift.

This means we can use protocols as parameters, return types on functions, var and let properties, and in collections (like arrays), as we’ve seen in our previous examples with the Restaurant protocol being used in our delegate, cell and/or ViewController.

Now, let’s see this concept in-depth:

a restaurant implementation
a restaurant implementation

We’ve created two restaurant implementations, one a chicken food restaurant, the second one a seafood restaurant. Let’s suppose that we need to display the menus of all the restaurants in our app.

a playground file using our func displayMenus

As we can see, we are using protocols as a type everywhere and we can use a specific instance of it, like ChickenFoodRestaurant or SeaFoodRestaurant.

Finally, these are the logs for that func.

display menu logs

This feature leads us to the next topic.

Protocol Polymorphism

The word polymorphism comes from the Greek word poly (meaning many) and morphe (meaning form).

Polymorphism lets us interact with multiple types through a single, uniform interface. In the object-oriented programming world, the single uniform interface usually comes from a superclass, while in the protocol-oriented programming world, that single interface usually comes from a protocol.

As we’ve seen in the previous section, we used a ChickenFoodRestaurant and SeaFoodRestaurant in an array, but both had a specific implementation of the display menu func.

This is the potential of polymorphism and protocols working together to create different implementations without forcing typecasting or duplicate code.

Protocol Typecasting

Typecasting is a way to check the type of an instance and/or to treat the instance as a specified type.

In Swift, we use the is keyword to check whether an instance is of a specific type and the as keyword to treat an instance as a specific type.

typecasting is keyword example

The is keyword returns true if the object is a specific type.

typecasting as keyword example

This keyword lets us typecast our protocols to validate if an object is of a specific type and use it as that type, as we can see in the example.

Protocol Generics

Generics and protocols can be a powerful tool if used correctly to avoid duplicate code. Assuming you have a general idea about generics, let’s quickly take a look at how to use it with protocols.

Protocol generics say: “We do not know the exact type to use; therefore, when a type adopts this protocol, it will define it.”

Protocols can use one or more associatedtypes. Let’s see an example:

a data source protocol with a generic item

So, once we need a specific data source, for example, a restaurant data source, we have a few options:

a concrete data source type
a generic data source type

Let’s see both data sources in action:

using data sources

Both are and work the same but there are a few differences once we design our apps in this way. We’ll talk about this topic later.

Protocol Extension

Protocol extensions make protocols an awesome tool. They allow us to add default implementation and values of protocol funcs and properties.

As you may notice, it is a useful tool to make requirements optional without the @objc and optional keyword. Protocol conforming types can provide their own implementations or use the default ones.

If you are familiar with the decorator pattern, you may know that protocol extensions allow us to add implementation of funcs not described in the protocol, “decorating it.

A great feature to add specific funcs to multiple conforming types without modifying each type. Did you see how we kill the classic base class inheritance here?

Let’s see an example of all these features modifying our Restaurant protocol declared before:

using extension to add default values and implementations

We’ve changed our Restaurant protocol, adding a default value for the menu property and default implementation for displayMenu. Let’s see how the concrete types, like our SeaFoodRestaurant and ChickenFoodRestaurant structs have been changed.

a conforming protocol using default values and implementations

Our SeaFoodRestaurant struct doesn’t need to define the menu because its default value is taken from the default restaurant value declared in the extension.

The same happens with the display menu func, it’s not required because we’re using the default implementation from the extension.

a conforming protocol using default values and implementations but overriding it

Now our ChickenFoodRestaurant gets the benefits from the default implementations and values but overrides its displayMenu implementation. Let’s see what happens when we call both objects:

a playground file using our func displayMenus
log

As we can see, ChickenFoodRestaurant is using its default implementation but the SeaFoodRestaurant uses the protocol default implementation.

protocol extension with default value as input parameter for a func

We added a new func named openingHours and in the extension, we gave its input day a default value (day = Date()).

The extensions give us the capacity of adding default values to our protocol func inputs. Anyway, if your conforming class overrides its implementation, it will have to declare the default values.

On the other hand, we declared the markFavorite func only in the protocol extension. This is a great way to add extra functionality to our protocols, working as a base class for all the objects that will conform to our protocol.

Ambiguous Method Implementation with Protocol Extensions and Polymorphism

Sometimes, when we are using extensions and polymorphism, we can get issues with ambiguous method implementation because we didn’t indicate clearly which implementation of the method it should use, getting a compilation error.

In this case, we have to add the implementation of the func to the conforming type. Let’s see an example:

new protocol with default implementation on the extension

What happens if we make our SeaFoodRestaurant from previous examples conform to this new protocol:

Ambiguous Method Implementations with Protocol Extensions and Polymorphism

We get an ambiguous implementation issue, how do we fix it? Our SeaFoodRestaurant must implement its version of openingHours(day: Date).

There are other confusing cases to explain and we’ve already seen it. As we were talking about protocol extensions, it allows us to add default implementations of some methods and add new method implementations as well, but what’s the difference?

When method declaration exists in the protocol

The protocol declares the openingHours(day: Date) method and provides a default implementation.

The method is overridden in the SeaFoodRestaurant implementation. So, the correct implementation of the method is invoked at runtime, regardless of the type of the variable.

When method declaration doesn’t exist in the protocol

Because the method is not declared in the protocol, the type is not able to override it. That is why the implementation of a called method depends on the type of the variable.

If the variable is of type SeaFoodRestaurant, the method implementation from the type is invoked. In case the variable is of type Restaurant, the method implementation from the protocol extension is invoked.

Method Dispatch, Dynamic vs. Static in Protocols

Method Dispatch is the way of choosing what implementation will execute when you invoke a method. When the compiler resolves that at compile time it is Static dispatch. When it is resolved at runtime, it is Dynamic dispatch.

In a brief, reference types use dynamic dispatch choosing the implementation at runtime from the inheritance hierarchy. The value types use static dispatch because they don’t need inheritance.

All the required protocol methods uses Dynamic Dispatch because the compiler has to look for the specific implementation of that method.

There are some exceptions for example, when you create a variable without type but which only conforms to the protocol instead, it is always created in the existential container which uses static dispatch.

ProtocolDispatches

The sambosTavern use the static dispatch and the bubbaGump use the dynamic dispatch.

Swift Standard Library Extensions

In addition to extending your own protocols, you can extend protocols from the Swift Standard Library. Let’s see a small example.

Do you remember our example of the chicken menu and slices? Here it is again:

displayMenusAndChickeSlice

Let’s see how we can change it, extending the Swift Standard Library:

extending the Swift Standard Library

This is a great way to get helpful mechanisms to power our own protocols.

Protocol Extensions vs. Base Classes

As we have seen, protocol extensions and base classes may seem quite similar, but there are several benefits of using protocol extensions.

Since classes, structures, and enums can conform to more than one protocol, they can take the default implementation of multiple protocols. This is conceptually similar to multiple inheritance in other languages.

Another benefit is that protocols can be adopted by classes, structures, and enums, whereas base classes and inheritance are available for classes only.

Tips for Good API Design

Now that you understand how protocols behave, it’s best to go over when you should use protocols. Some recommendations for the best use of protocols:

  • Start with concrete use cases to move into protocols.
  • Consider composing new protocols from existing protocols defined in the standard library. Refer to the following Apple documentation for a good example of this.
  • First, explore the use case with concrete types and understand what code it is you want to share and find where it is being repeated. Then, factor that shared code out with generics. It might mean you need to create new protocols. Discover a need for generic code.
  • Instead of a generic protocol, consider defining a generic type instead.

Protocol Witness

In previous versions of swift (<5.3) when we try to use a protocol with enums, Swift restrict the protocol conforming matching model forcing us to use a manual implementation instead use the enum cases with the same name and arguments as the protocol, like this example:

Protocol Witness

Swift 5.3 has lifted that restriction so we can make our enums to conform protocols matching the vars and funcs as enum cases, like this example

Enum Protocol Witness

Conclusion

Here you have some powerful tools to start using protocols.

There are more benefits and mechanisms like copy on write and conditionals that we didn’t see in this article, but with all the information here, you should be able to go deeper with the use of protocols and maybe this article stimulates your curiosity.

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

Responses (1)

Write a response

Probably the best most thorough survey of Swift Protocols I have come across. Thank You!