Swift Core Concepts: Mastering Protocol

Tifo Audi Alif Putra
Geek Culture
Published in
6 min readJul 22, 2021

One of the most critical feature in Swift, Protocol :]

Photo by Med Badr Chemmaoui on Unsplash

Overview

Protocol and Protocol-Oriented programming are the important feature in Swift programming language. Imagine if we don’t have a protocol in Swift, we are facing many difficulties for building a scalable system. We can’t implement delegation pattern, we can’t make an abstraction, and our system is not easy to refactor and has a high vulnerability. So in this article, I want to have some a good refresh about protocol, how we can use it, and understanding how it actually work by knowing the difference between static dispatch and dynamic dispatch. Mastering protocol is quite important to us as an iOS developer in order to create a high-quality system.

Getting Started with Protocol

Let say we have a single line of code bellow:

profileViewController.showLoading()

From the code above, we can assume that profileViewController is an instance from ProfileViewController class and showLoading() is an instance from a function inside the ProfileViewController class. Swift is work by using something called type-system, it will check whether the ProfileViewController has a function called showLoading() or not. If you have some experience using Objective-C, if we are not define the showLoading() function inside the ProfileViewController class, the compiler will happily run the program. But we will get an error when using Swift because basically Swift is a strong-type programming language, and it will prevent us from stupid error like forget to define the showLoading() function inside the class.

But how can we implement showLoading() function to another type? maybe we have some view controllers like HomeViewController, or ExploreViewController? Can we implement showLoading() function without define it one by one to each types? Then protocol come in as a hero.

As our previous discussion, Swift will verify whether each type has the showLoading() function or not. If it not then the compiler will tell us some errors. With protocol, we can define showLoading() function not only to specific one type. Whatever type that conform to Loadable protocol then it must implement the showLoading() function. This is the base usage of protocol, let’s deep dive and see what is others benefit from protocol and configure how we can implement it in real-world use case scenario.

Abstraction: Hiding Concrete Implementation

Protocol is a tool that we can grouping some types and will have something called super-type which is the protocol itself and we can hide the concrete implementation of each types. For example, let say we have two classes which is Square and Rectangle. We all know that Square has a requirement that each length of the side must be equal. We also know that basically Square is also a Rectangle. In order to calculate the area, we use same way between the Square and Rectangle, which is side x side. Then we can create a protocol called Rectangle, we can grouping the Square and Rectangle classes to become one super-type called Rectangle. With this approach we only need to write one implementation to calculate the area. Protocol allow us to feel don’t care about the detail implementation about the area calculation from Square or Rectangle. We only need to define the requirement inside the protocol, then we can make it flexible and easy to maintenance.

Protocol Syntax

In this example, we will define basic implementation to translate the language using protocol. Look at the Localizable protocol, it only has one requirement which is static variable with type of array of Language. It only need the getter and we don’t need to define the setter. We can conform to the Localizable protocol and fulfill the requirement with computed-property or we also can use the private(set) access modifier. We also need to make the variable as a static while implementing it to our types.

Then we have a ImmutableLocalizable protocol and it also conform to the Localizable protocol. If we want to conform to ImmutableLocalizable protocol, then we need to fulfill two requirements:

static var languages: [Language] { get }
func translate(to language: Language) -> Self

The translate function return Self, which mean it will return the current type that conform to the protocol. For example if we have a UILabel class and it conform to ImmutableLocalizable protocol, then the Self type is a UILabel itself.

Last, we have a MutableLocalizable protocol. Actually this protocol is little bit similar to ImmutableLocalizable protocol, if you realize there is a difference that we put the mutating keyword to the MutableLocalizable function requirement. Mutating is used inside the struct if we want to modify the property value inside the struct body. We don’t need to add mutating keyword inside the class because it basically by default is already mutable.

Implementing Protocol to Types

With some protocols that we already made before, let’s try to implement it to the types.

From above implementation, we already fulfill the requirements by defining the languages static variable and translate function with mutating keyword in the front. We also can implement protocol with the extension from the type.

But is it possible to give a default implementation to the protocol so we don’t need to provide the implementation inside the types that we have? Yes it is.

Another question, is it possible to give the protocol some constraint? maybe it only the UIViewController and to its sub type only to be able conform to the protocol? Yes it is :] .

But How Actually the Protocol Work? :]

From the previous explanation about protocol, honestly is it more than enough for us to be able implementing it to the real world use case in our job as an iOS developer. But if you want to become a real engineer, stay with me, don’t stop to read this, let’s configure how the protocol work, configure the performance, and what are things that need to be consider. We need more deeper to know how Swift compiler is work.

Static Dispatch vs Dynamic Dispatch

Specifically we need to know what happen if some function is invoked in Swift. There are two mechanism in Swift about the function call, which is static dispatch and dynamic dispatch. Static dispatch is pretty straight-forward, it is used to some function that will never change or it is already mark as a final. It can be found in some scenario like global function, function inside struct, or function inside a class that marked as a final. What is the meaning of function will never change? basically it is the function that never be override, compiler as a high level will hardcoded the function address and hold the reference from it when the function is called in Swift.

Then how about dynamic dispatch? well it is a little bit complicated compared from the static dispatch. A function call would be a complicated things if we implement the inheritance or conforming to the protocol. Compiler doesn’t know exactly where the function come from, is it from super class? or is it from the child class? or is it from the protocol default implementation like we discussed from the previous example?

Well the swift compiler use something called witness-table. This table would created every time when we create a new class. Every functions inside the class will saved to the table. Then the child class will have a copy value from the table and it will replace the function that it need to override. Swift use this table at runtime. When function is called, then Swift will use the table to configure the source from the function.

There are some disadvantages of using the dynamic dispatch like the compiler cannot doing some optimization because every time the function is called it require a constant overhead cost. We can conclude that the dynamic dispatch is slower than static dispatch.

Dispatch in Protocol

A function dispatch in protocol is a little bit similar from the previous explanation. Every type that conform to the protocol will have something called protocol-witness table. Every requirements inside the protocol either variable or function will have a row inside the table. This table will saved along with the instance that conform to the protocol. Like the previous example, Swift will use this table in a runtime. Compiler will find the function from the table and will call it. If we use instance from a class, the compiler will find the function from both the class table or the protocol witness table dynamically to find the correct source implementation.

Where to go from here

Congratulation my friend for read this article, I hope it will useful for you. Protocol is an important feature in Swift programming language. We can use it to solve so many problem in a real world scenario. Let me know if you have a question or maybe a feedback for this article. It would be really appreciated. I think I will continue to write the core concepts about Swift maybe like generic, memory management, or even high order function. I will let you know if I created new article through my linkedin. Thank you :] .

--

--