How to Master Protocols in Swift
Protocol definitions, inheritance, extensions, and more
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 class
types can use base classes and inheritance from a protocol.
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.
Let’s make an example:
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.
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.
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.
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.
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:
Now, let’s make a few changes in our struct, it’s time to use protocols:
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:
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.
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.
Now our ViewController
will look like this:
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:
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.
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.

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.
The is
keyword returns true
if the object is a specific type.
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 associatedtype
s. Let’s see an example:
So, once we need a specific data source, for example, a restaurant data source, we have a few options:
Let’s see both data sources in action:
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:
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.
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.
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:

As we can see, ChickenFoodRestaurant
is using its default implementation but the SeaFoodRestaurant
uses the protocol default implementation.
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:
What happens if we make our SeaFoodRestaurant
from previous examples conform to this new protocol:

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.
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:
Let’s see how we can change it, 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:
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
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.