Protocol-Oriented Programming in Swift – Part 1
I’m Charles Nyisztor, and I welcome you to this new tutorial on Swift programming.
In this video, I am going to talk about the protocol-oriented programming, a new paradigm introduced by Apple at the World-Wide Developer Conference in 2015, along with Swift 2. If you take a closer look at the Swift standard libraries and at Swift itself, you’ll notice the high number of protocols. So this is a pattern that we should also follow.
Now, let’s go through some of the core Protocol-Oriented Programming concepts.
With protocol extensions, we can provide default method and property implementations to all conforming types. Without protocol extensions, we would have to provide the implementation in each class, or create a common base class and define the common methods and computed properties there.
Protocols can inherit from other protocols. Unlike with classes, a protocol can inherit from multiple protocols. Each protocol in the inheritance hierarchy declares additional requirements.
Swift does not allow multiple inheritance for classes, types, and enums; however, Swift types can adopt multiple protocols via protocol composition.
Protocol composition is a powerful feature which stands at the core of the protocol-oriented programming. Breaking our requirements into different protocols produces a cleaner design, where responsibilities are well-defined, and we can avoid the proliferation of bloated, monolithic types.
To recap: Protocol extensions, protocol inheritance, and protocol composition are the essential concepts behind protocol-oriented programming. I am going to explain each of these concepts in this series on Protocol-Oriented Programming in Swift.
First, let’s explore protocol extensions. In the following demo, we are going to define a GameUnit protocol and three different implementation classes which adopt this protocol. I am going to show you how protocol extensions help us write less cluttered code.
All right then, let’s switch to XCode.
Let’s create our GameUnit protocol. Note that the code in this sample is meant solely for demonstration purposes.
The GameUnit protocol declares an ID property which will act as a unique identifier.
Next, I declare the isAlive and the healthLevel properties. Finally, I declare a hitTaken method, which will affect our unit’s health level and isAlive status. The mutating keyword denotes that the method changes the state of the adopting type.
Now I am going to implement the first class which adopts the GameUnit protocol. The Peasant class must implement all the properties, and the hitTaken method declared in the GameUnit protocol.
I assign a low healthLevel to this class. The hitTaken() method decreases the healthLevel by one whenever the method is called.
If the healthLevel becomes zero, the isAlive state switches to false. The console message reflects the Peasant objects health status accordingly.
Next, I am going to create a Soldier class. It also adopts the GameUnit protocol, so we have to define all the properties and the hitTaken method as well.
The implementation is very similar to what we’ve got for the Peasant class, except that the soldier has a higher healthLevel.
Finally, I am going to implement a Knight class. Again, the code is almost the same as for the Soldier and the Peasant class, except for the default value of the healthLevel property.
We’ve got one protocol and three different classes which adopt this protocol. Even in this very small codebase, there is a lot of redundancy: the hitTaken() method is the same in each class.
We could introduce a common base class and move the hitTaken() method there; however, Swift offers us a better way to solve this: protocol extensions come to the rescue!
I declare an extension for our GameUnit protocol. Protocol extensions provide common functionality to all types that conform to the given protocol.
Then, I add the implementation of the hitTaken() method.
Now we can safely remove the hitTaken method from our classes. The default implementation from the protocol extension will automatically apply.
We can override the default implementation by implementing the hitTaken method in any of our classes.
This is what I am going to do in the Knight class.
Let’s check out the console: the Peasant and the Warrior objects use the default implementation provided in the GameUnit protocol extension, whereas the Knight uses its own implementation.
The functionality defined in a protocol extension can be constrained to a given type using the ‘where’ keyword, which allows us to simplify our Knight class, too.
I am going to implement a GameUnit extension constrained to the Knight class and its subclasses.
Here we go…
We managed to remove the hitTaken method from the Knight class, and everything works just as before.
Protocol extensions are a very useful feature, which can be used to remove redundant code and produce less-cluttered classes.
Thank you for watching this tutorial on protocol extensions. In the next part of the series, we are going to explore further protocol-oriented programming concepts.
Don’t forget to subscribe to my youtube channel if you are interested in Swift-programming related tutorials. I offer valuable content for free – enjoy!
See you soon!