This three-part series on delegation is heavily "inspired" by the brilliant tutorial written by Andrew Bancroft. If you find any of this helpful, he deserves all the kudos, I merely mixed my notes with his ideas to create a permanent written record of how I have come to understand delegation.
Delegation is a design pattern heavily used in iOS and OS X APIs by Apple and third-party developers.
Design implies architecture. It connotes a strategy for organising something. Design conveys the method by which components will work together to accomplish an end.
Pattern implies that there is some repeatable process that has honed in around a common thread… a common practice… a predicable method for doing something. “Pattern” gives the impression that such a practice has converged over time into something that is now well-known, well-understood and commonly used.
A design pattern in software then is a method for architecting, strategising about and organising an application’s code in such a way that has been found to be commonplace, repeatable and practically sound over time.
In the real world, the word delegation encapsulates relationship and responsibility. A delegator/principal (noun) would delegate (verb) control or responsibility to another person called a delegate.
We map this very nicely on to software. A class can delegate control or responsibility, meaning behavioural logic, to another class called a delegate. But what kinds of responsibilities and concerns?
How is delegation used?
Delegation is most commonly used as a way for one class to communicate to another class. E.g. UITableViewDelegate methods:
- tableView(_:willSelectRowAtIndexPath:) - tableView(_:didSelectRowAtIndexPath:) - tableView(_:didHighlightRowAtIndexPath:)
Looking at Apple APIs it’s clear that one of the intended uses for delegation is to allow one instance to communicate back to some other instance that something will/did happen. Although unlike NSNotificationCenter, delegation fits the scenario where an instance only needs to send a message to a single listener (the delegate) instead of broadcasting the message throughout the app.
Another common usage for delegation is to provide a delegate instance the opportunity to customise certain aspects of the delegating instance’s internal state. E.g. UITableViewDelegate methods:
- tableView(_:heightForRowAtIndexPath:) - tableView(_:editActionsforRowAtIndexPath:) - tableView(_:indentationLevelForRowAtIndexPath:) - tableView(_:shouldHighlightRowAtIndexPath:)
These are all customisation-points that UITableView allows its delegate to have a say in. Some of the methods are so important that a table view can’t render itself properly unless it gets this information from the delegate. But why did Apple decide to implement UITableView and other Cocoa Touch APIs in this way?
Good question. Consider an off-the-shelf Cocoa object such as a table view (UITableView). These objects are designed to fulfil a specific role in a generic fashion; a table view for example show a scrollable table but has no idea how many rows should the table have or what data to put in each of those rows. This restricted and generic behaviour necessarily limits what an object can know about how an event affects (or will affect) something elsewhere in the application, especially when the affected behaviour is specific to our application. Delegation provides a way for our custom object to communicate application-specific behaviour to the off-the-shelf object.
Read next: How delegation works?