Delegation Pattern in Swift
I decided to make this article a part of a design pattern series for iOS development. This will be the first article for the series and the most used one as my opinion.
Some can argue that design pattern is the essence of the object oriented programming since they are well structured solutions to common algorithm problems. In this article, I will try to explain the Delegation Design Pattern with real life code examples used in my own iOS projects.
One of the core concepts of iOS programming with Swift is Delegation. In order to really grasp the idea of Delegation in Swift, one should be familiar with Protocols in Swift.
When I started to learn Swift for iOS development. First, I didn’t really comprehend what a protocol is. It was a weird terminology for me that I did not see in any other software development language before. I mean that if someone is familiar with one or two software development language, the one can be familiar with the terminologies like class or struct but a protocol was an unknown word for me. Although it sound different, If you work with JAVA or a similar OOP language there is a very simple explanation what a Protocol is; “Interface.” Like interfaces in most OOP languages, protocols are blueprints that the classes, structs or enums that conforming the protocols can do. It carries the signatures of functions and properties that the conforming objects have to implement. As you can see, they are pretty much interfaces of the other common languages.
For more formal explanation what protocols are here is the explanation of swift.org;
A 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.
To sum all of the explanations up, here is a most basic example of a simple protocol in swift.
There is a lot more to know about protocols but the explanation above should be enough to grasp the essence of it in order to understand delegation pattern.
Once one get to know protocols, we can proceed to Delegations, what are they, where and when to use them. Delegation is a concept that mostly used in Protocol Oriented Programming (POP). The concept of delegation is widely used in original Apple libraries in Swift. One simple example for this is UITableView. When one wants to show some data in a row by row manner, one can use a preconfigured view type called UITableView and in order to manipulate the rows in the UITableView, the containing controller needs to conform UITableViewDelegate protocol. Apple always designs their views in a protocol oriented way and uses a lot of delegates like in UITableView.
Even in the most simple usages of an UITableView in Swift, we need to conform to Apple’s UITableViewDelegate protocol to manipulate the cell actions as above. For example; to determine what happens when someone tap on the cell of the UITableView. Apple delegated this action handling to the UITableViewDelegate Protocol with the usage of Delegation Pattern.
After understanding that Apple really loves delegation pattern, now it is time to understand when and where it is best used.
As the name implies, delegate means transferring some responsibilities to some other object. Most common practice of this responsibility transfer is when we use UITableViewController with Custom UITableViewCells. I am going to explain this concept with a widely used example with real life scenarios.
Let’s say that you are creating an app that contains a row by row information like a list of movies. The easiest way to implement that is to use a UITableView, right? Also, one can like some movies more than others, and want to implement that functionality to your app. In custom cell view, you add a “Like icon” to the cell. The idea is when someone taps the like icon, the app should add that movie into the favorite movies list since you want to display only favorited movies eventually. No one wants to see only awful movies, right?
Now, your “like icon” — which should be a button to be tapped conveniently — is in the UITableViewCell which is in a different swift file then your UITableView. So the liking or adding to favorites action should be trigger from UITableViewCell but you do not want to store FavoriteMovies Array in the UITableViewCell file right? Because it not specific per cell, it belong to the whole table view. So, we should somehow send the “adding to favorites” (or liking) responsibility to the main UITableView from the UITableViewCell. This is the time that delegations come to place.
Let’s see this with an example first, then explanation will come later. I will use Swift UIKit with programmatic approach.
After adding a Favorite (or Like) button to the cell view, we need to create a protocol to be conformed according to delegate pattern later. We need to add the signature of the action we want to perform when the favorite button is tapped on with the Cell parameter, which in our case is handleFavoriteTapped(_ cell: MovieCell). We would like to pass the object which we want to get the required information from to the parent object, and that object is the Movie object in our case, but I like to pass the whole cell object for the ability to access more information of the class if required.
When declaring the protocol, I extend it from AnyObject in order to allow it to be conformed only from classes. The reason I want the protocol can only be conformed from class is because I want to declare the instance of my protocol as a weak variable, since weak cannot be applied to non-class type. The usage of weak is primarily for preventing potential memory leaks and retain cycles. (We will be explaining the concepts of memory leaks, retain cycles in another blog post)
After creating the protocol, we need an instance of it in the cell class since we will call the function from it when the favorite button is tapped. (weak var delegate: MovieCellDelegate?)
The only thing left is to call the function in our protocol from the handleFavoriteButtonTapped() when the favorite button is tapped.
In order to use the delegated action from another class, we need to conform the MovieCellDelegate protocol and implement the handleFavoriteTapped function signature as follows.
This was the most common usage of the delegations in Swift. To sum up, you want to perform an action in child object, UITableViewCell and you want to change something with this action on parent object, here a.k.a UITableView. You delegate the responsibility of the action from the child object to the parent object like above.
In the last part, I would like to discuss the benefits of using the delegation design pattern. Because the example from above is only one of the solutions to this problem, however I think is the best one. The main benefit of delegating certain decisions and behaviors to the parent object from the child object is creating a decoupled model with low dependency. Hence, it becomes much easier to support multiple scenarios without creating large classes just to provide all the required behaviors in the child objects.
As I mentioned, delegation is just one way of doing this data passing. There are multiple ways that we could have implemented the same behavior above. I will mention some of the alternative ways to implement the same result below.
Using Notifications instead of Delegation
There is another pattern that can be used to pass data to other objects called Notification Pattern. You could easily attach a notifier onto movie object in each cell and once the movie object is changed, it could pass the information to the observer objects. That could work but there are two major downsides to this approach.
First is, notification patterns are used for broadcasting information. That means that, if there is many-to-one or one-to-many relationship using notifications is logical, since we pass the data in one-to-one manner, using delegations is more convenient.
Secondly, one should be careful using notifications, because notifications — if not used correctly — are CPU-intensive tasks. A simple task like above, there is no need for heavy lifters like notifications.
Using Closures instead of Delegation
A viable alternative for the delegation pattern is simply using closures. Instead of calling a delegate function, the delegating class calls a closure that’s defined beforehand as a property on the delegating class. You could easily use closures when passing data from one object to another asynchronously. Closures, like delegation, can provide lightweight and decoupled solutions to this problem. The only downside would be, if your logic grows to much, closures could make a mess in regards to readability.
This was a simple explanation on what delegation is and how to utilize them the most. There are still a lot to offer of delegations though. I hope this has been helpful. Stay healthy.