Protocols in Swift
What is Protocol?
In simple terms a protocol is an interface which defines properties and methods. So any type that conforms to the protocol should implement the methods and properties requirements that are defined.
Protocols - The Swift Programming Language
A protocol defines a blueprint of methods, properties, and other requirements that suit a particular task or piece of functionality.
protocol Car {
var topSpeed: Double { get }
func noOfDoors() -> Int
}
What we can achieve using Protocols?
In object oriented programming a class can inherit only one class i.e a class cannot extend multiple classes. So multiple inheritance is not possible, however a class can extend multiple protocols
For instance
class Car {
var topSpeed: Double
init(topSpeed: Double) {
self.topSpeed = topSpeed
}
func noOfDoors() -> Int {
return 4
}
}
class AirCraft {
var airspeedVelocity: Double
init(airspeedVelocity: Double) {
self.airspeedVelocity = airspeedVelocity
}
func noOfWings() -> Int {
return 3
}
}
There are two classes Car
and Aircraft
So we can subclass different Cars (BMW, Honda, Toyota etc.) and Aircarfts (Boeing 707, Airbus A330 etc.) What if there is a Car which is also an Aircraft?
class FlyingCar: Car, AirCraft {
}
Oops that’s the issue and its not supported in object oriented programming. Let's assume if we want to provide the implementation as following:
class FlyingCar: Car {
var airCraft: AirCraft? = nil
override func noOfDoors() -> Int {
return 2
}
}
let flyingCar = FlyingCar(topSpeed: 100)
flyingCar.airCraft = AirCraft(airspeedVelocity: 100)
print(flyingCar.noOfDoors())
print(flyingCar.airCraft?.noOfWings())
The code is not readable and creates the strong dependency. That's why is it not allowed in OOP. So using protocols we can create a FlyingCar
that conforms to both the protocols Car
and AirCraft
protocol Car {
var topSpeed: Double { get }
func noOfDoors() -> Int
}
protocol AirCraft {
var airspeedVelocity: Double { get }
func noOfWings() -> Int
}
class FlyingCar: Car, AirCraft {
var topSpeed: Double
var airspeedVelocity: Double
init(topSpeed: Double, airspeedVelocity: Double) {
self.topSpeed = topSpeed
self.airspeedVelocity = airspeedVelocity
}
func noOfDoors() -> Int {
return 2
}
func noOfWings() -> Int {
return 2
}
}
let flyingCar = FlyingCar(topSpeed: 100, airspeedVelocity: 100)
print(flyingCar.noOfDoors())
print(flyingCar.noOfWings())
Voila! Looks clean. So Protocols solve the problem of multiple inheritance.
Protocol Extensions
Extensions - The Swift Programming Language
They allow us 2 things:
1) Makes properties and methods of protocol 'optional' by providing the default implementation. i.e Types that conforms to protocol can use default implementation or provides their own implementation.
2) Adds specific methods to multiple types that already conform to protocol without modifying the types individually!
Default implementation example
enum AppError: Error {
case any
var description: String {
return "any error"
}
}
protocol ErrorHandler {
func onError(error: AppError)
}
class Handler: ErrorHandler {
func onError(error: AppError)
print("Handler onError call \(error.description)")
}
}
In above example the Handler class conforms to ErrorHandler protocol and provides its own implementation. So we can provide the default implementation of protocol as following which makes its implementation as optional.
extension ErrorHandler {
func onError(error: AppError) {
print("ErrorHandler onError call \(error.description)")
}
}
class Handler: ErrorHandler
// No need to provide implementation if default implementation is needed
}
Protocol and Polymorphism
Polymorphism means having many forms. Let's see how Protocol changes the behaviour when the default implementation is provided and without default implementation.
protocol ErrorHandler {
func onError(error: AppError)
}
extension ErrorHandler {
func onError(error: AppError) {
print("ErrorHandler onError call \(error.description)")
}
}
class Handler: ErrorHandler {
func onError(error: Error) {
print("Handler onError call \(error.description)")
}
}
let handler: Handler = Handler()
handler.onError(error: AppError.any)
The output to the console is "Handler onError call any error". Now remove method declaration from ErrorHandler protocol
protocol ErrorHandler {
}
The output to the console is "Handler onError call any error". So does it mean there is no difference? No there is a difference.
let handler: ErrorHandler = Handler()
Now the output to the console is "ErrorHandler onError call any error"
If we add method again
protocol ErrorHandler {
func onError(error: AppError)
}
The output to the console is "Handler onError call any error"
Method declaration in protocol
So if method is declared in protocol and default implementation is provided the method is overridden in Handler
method implementation. It decides at runtime irrespective the type of variable
Method is not declared in protocol
As method is not declared in protocol the type is not able to override it. So the implementation depends on the type of a variable. If the variable is of type Handler
, then its method implementation is invoked. If the variable is of type ErrorHandler
, then its method implementation is invoked.
How Swift provides solution to resolve ambiguity using Protocols
Suppose there are 3 protocols with the same method signatures
protocol A {
func show()
}
protocol B {
func show()
}
protocol C {
func show()
}
These protocols have the default method implementation
extension A {
func show() {
print("show A")
}
}
extension B {
func show() {
print("show B")
}
}
extension C {
func show() {
print("show C")
}
}
Now there is a type which conforms to these protocols
struct S: A, B, C {
}
In this case type is not sure which implementation from the protocols A, B, C
it needs to implement, that will result in compilation error. To fix this error we need to provide an implementation to type S
struct S: A, B, C {
func show() {
print("show S")
}
}
Swift handles this with ease compare to other programming languages by allowing programmer to take the control where the compiler fails.
Conclusion
Protocol is powerful concept of Swift programming language. Unlike other programming languages using protocols and protocols extensions swift provides a type safety at compile time that ensures the reusability and maintainability.
I hope you like my first article. Welcome any feedback. Thank you.