Delegates Are Not Notifiers
You're an iOS developer. How would you set up a class to notify another object of an event?
In the context of iOS development, a few options may come to mind when
answering this question: NotificationCenter
,
blocks/closures, or delegation. By far, the most popular answer I've
received in interviews is delegation, which is understandable given
the frequent use of the delegate pattern in iOS APIs. However, there
is a difference between notification and delegation, just as the names
imply.
Two Obvious Differences
When asking why you might prefer notifications or delegates, a common
observation is that notifications can be sent to multiple targets, but
delegation only allows for a single target. So if an object is using
a delegate protocol as a means of notification, it can only have one
object listening for those "notifications". Posting notifications
through NotificationCenter
allows for multiple listeners.
Someone who has a little more experience might also recognize that
NotificationCenter
provides loose coupling between the
object that sends the notification and the object that responds
to it. Neither object needs to know anything about the other. With
delegation, there is often tighter coupling between the class that
receives the delegate and the implementation of the delegate protocol.
Something Deeper
However, these observations actually point to conceptual differences between notification and delegation. It can be a bit misleading to think of delegation as a means of sending notifications between two objects. Rather, delegation should be thought of as a means for implementing functionality that an object leaves undefined. One object is delegating certain responsibilities to another object, which allows the object to become more flexible and general-purpose. The Apple documentation expresses it this way:
Delegation is a simple and powerful pattern in which one object in a program acts on behalf of, or in coordination with, another object.
Consider UITableView
and its delegate,
UITableViewDelegate
. Every table view must be able to create
and configure the cells it displays. However, it's not possible for
UITableView
to know how to create and configure custom table
view cells. Therefore, UITableView
delegates that
responsibility to another object through UITableViewDelegate
.
Using delegation allows you to compose complex objects from simpler
objects. In the UITableView
example, we are composing a
complete table view implementation from an instance of
UITableView
and object instances that implement
UITableViewDelegate
. In theory you can swap out distinct
implementations of UITableViewDelegate
and/or
UITableViewDataSource
. In such a scenario, it should become
clear that delegation is not used for notification but for rounding out
functionality that isn't known to the delegating object. The object
and the delegate work together as a cohesive unit, each necessarily
complementing the other.
Alternate Approaches
In some respects, delegation provides functionality similar to inheritance in object-oriented class heirarchies. A base class may define abstract methods that are implemented in derived classes. The distinction becomes one of using composition (via delegates) versus inheritance (via class heirarchies). One of the benefits of using composition is that delegate implementations can be used with other objects that use the same delegate interface and delegate implementations can be swapped out without needing to recreate derived class instances.
emailTableView.dataSource = personalAccountDataSource emailTableView.delegate = detailedCellDelegate emailTableView.dataSource = businessAccountDataSource emailTableView.delegate = summaryCellDelegate
In this snippet of code, we can see the possibility of completely rebuilding a table view by simply switching its data source and delegate implementations.
A Source of Confusion
One of the reasons we might confuse delegation with notification is that iOS delegates often have a "notification feel" to them. For instance, the table view delegate defines the following methods:
func tableView(UITableView, willSelectRowAt: IndexPath) // Tells the delegate that a specified row is about to be selected. func tableView(UITableView, didSelectRowAt: IndexPath) // Tells the delegate that the specified row is now selected. func tableView(UITableView, willDeselectRowAt: IndexPath) // Tells the delegate that a specified row is about to be deselected. func tableView(UITableView, didDeselectRowAt: IndexPath) // Tells the delegate that the specified row is now deselected.
Because these methods are called in response to user interaction with the table view, they seem to be notifications that are triggered by a user-initiated event.
The Nature of Notifications
How can we draw a distinction between delegation and notification if the line between them can become so fuzzy? Here's another comment from the Apple documentation:
These recipients of the notification, known as observers, can adjust their own appearance, behavior, and state in response to the event.
There's an interesting choice of phrasing there - "[they] can adjust their own appearance, behavior, and state." When we implement a notification observer, it responds to events by altering itself. When we implement a delegate, it responds to events by altering the appearance, behavior, and state of its associated object.
Summary
Okay. Delegates aren't notifiers. But do I really care? Maybe not. If you know how to do what you want to do, perhaps it doesn't really matter. However, when we're building applications with other engineers, it can become important to communicate in a way that is clear to both parties. If I say we should implement a notification for an event, I'm not suggesting a new delegate method. Similarly, if you suggest adding a delegate, you're envisioning two tightly coupled objects that are working together.
So here are a few ideas you can keep in mind regarding common distinctions:
Delegation | Notification |
---|---|
Tightly coupled objects | Loosely coupled objects |
Single delegate | Multiple observers |
Alters sender's behavior and state | Alters the receiver's behavior and state |
During my early experience with iOS, I was frustrated by some common uses of delegation and really wanted one-to-many notifications. However, understanding the intention behind delegation (it's completing functionality, not sending a notification) has gone a long way to improving my understanding when building iOS apps.
Further Reading
DelegationNotification
Delegation pattern
Observer pattern