Why, When and How to use protocols in Swift

According to the Swift documentation:

protocol defines a blueprint of methods, properties, and other requirements that suit a particular task or piece of functionality. The protocol can then be adopted by a class, structure, or enumeration to provide an actual implementation of those requirements. Any type that satisfies the requirements of a protocol is said to conform to that protocol.

In addition to specifying requirements that conforming types must implement, you can extend a protocol to implement some of these requirements or to implement additional functionality that conforming types can take advantage of.

Can we cut the long story short?

Protocols can be seen as communication tool. They help communicate requirements and functionalities between components such as classes and enumerations. They are more or less like Interface in Java/Kotlin.

You define protocols in a very similar way to classes, structs, and enums:

protocol SomeProtocol {
    // protocol definition goes here
}

In this article, I will do justice to using Protocols for Delegation. Delegation is a design pattern that enables a class or structure to hand off (or delegate) some of its responsibilities to an instance of another type. This design pattern is implemented by defining a protocol that encapsulates the delegated responsibilities, such that a conforming type (known as a delegate) is guaranteed to provide the functionality that has been delegated. Delegation can be used to respond to a particular action or to retrieve data from an external source without needing to know the underlying type of that source. (Swift Doc)

Delegation is like giving a component the ability to communicate or interact with another component, without necessarily giving the component access to modify or even see the other component’s content.

Delegation Example

I am going to describe an example of a situation where using protocol for delegation is important.

If you’ve ever worked with UITableView and UITableViewCell, you will notice there exists a didSelectRowAt function that is called when a cell is selected. However, there is no function that handles when you interact with the subviews of the cell, like tapping a button in the cell or tapping an option menu icon in the cell. This is required to be handled in your custom UITableViewCell class. With that being said, it seems easy to handle a button tapped action by simply linking @IBAction from the view to the class and giving it a function name, once such button is tapped, the function specified is triggered.

This is where it gets interesting. Let’s assume you want to perform an action when the button is tapped. Probably you are working with a list of posts and you have a button that says Add to Bookmark. This implies that you probably want to add such a post to a database when the button is tapped. If you know using protocol delegation is the way to go, kudos to you ?. If you are wondering how protocol delegation will come in, you will learn how to go about it now, keep reading.

@IBAction func bookmarkButtonPressed(_ sender: Any) {
    // Code goes here
}

The above code describes the function that is triggered when the Bookmark button is tapped. Now, let’s add protocol delegation.

protocol PostTableViewCellDelegate: class {
    func didPressAddToBookmark(post: Post)
}

class PostTableViewCell: UITableViewCell {
    var post: Post!
    weak var delegate: PostTableViewCellDelegate?

    ...
    
    @IBAction func bookmarkButtonPressed(_ sender: Any) {
        delegate?.didPressAddToBookmark(post: post)
    }

}

Here, we declare a protocol and a function we want to call when the Bookmark button is tapped. You should notice a weak var of the delegate that is yet to be initialized. This delegate will be initialized by any View that tries to conform to the protocol. And with the delegate variable, we call the function declared in the protocol block.

class PostListViewController: UIViewController {
    
    ...
}

// MARK: UITableViewDataSource, UITableViewDelegate

extension PostListViewController: UITableViewDataSource, UITableViewDelegate {
    
    // Other UITableView initialization goes here
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "PostTableViewCell", for: indexPath) as? PostTableViewCell
        cell?.delegate = self // This is where we initialize the delegate
        
        return cell ?? UITableViewCell()
    }
    
}

// MARK: PostTableViewCellDelegate

extension PostListViewController: PostTableViewCellDelegate {
    
    func didPressAddToBookmark(post: Post) {
        // Database implementation code goes here
    }
    
}

Above is my ViewController that conforms to the protocol PostTableViewCellDelegate. We initialize the delegate variable declared in the UITableViewCell and then set it to self, knowing that we have implemented the protocol in the ViewController. Then, we implement the function declared in our protocol and place our database implementations there to add the post to bookmark. Soft!

Let me try and go over what we have done again. We have an @IBAction function that is called when our Add to Bookmark button is tapped. Then, with the delegate variable declaration, we call the function declared in our protocol block. Automatically, that triggers the function to be called in the ViewController that conforms to our protocol. Cool right?

In case you don’t understand it fully on your first try, do it over again and keep using it. You will finally get hold of it.


Protocol delegation is not limited to UITableViewCell or UICollectionViewCell, the same is applicable to building a custom view and exposing actions from the custom view to other views higher in the View Hierarchy.

Protocol usage is not limited to delegation alone as I said initially in this post. There are lots of other ways to make use of protocols. A very good example is in the VIP Architecture. Protocols form a very strong foundation for Clean Swift. I will explain how that works in upcoming articles.

Keep exploring, keep learning and keep pushing.

Cheers!