Design Patterns: Delegation

The delegation pattern is among the most common patterns in iOS and OS X development. It is a simple pattern that is heavily used by Apple's frameworks and even the simplest iOS application leverages delegation to do its work. Let's start by looking at the definition of delegation.

1. What Is Delegation?

Definition

The definition of the delegation pattern is short and simple. This is how Apple defines the pattern.

A delegate is an object that acts on behalf of, or in coordination with, another object when that object encounters an event in a program.

Let's break that down. The delegation pattern involves two objects, the delegate and the delegating object. The UITableView class, for example, defines a delegate property to which it delegates events. The delegate property needs to conform to the UITableViewDelegate protocol, which is defined in the header file of the UITableView class.

In this example, the table view instance is the delegating object. The delegate is usually a view controller, but it can be any object that conforms to the UITableViewDelegate protocol. If you're unfamiliar with protocols, a class conforms to a protocol if it implements the required methods of the protocol. We'll look at an example a bit later.

When the user taps a row in the table view, the table view notifies its delegate by sending it a message of tableView(_:didSelectRowAtIndexPath:). The first argument of this method is the table view sending the message. The second argument is the index path of the row the user tapped.

The table view only notifies its delegate of this event. It is up to the delegate to decide what needs to happen when such an event occurred. This separation of responsibilities, as you'll learn in a moment, is one of the key benefits of the delegation pattern.

Advantages

Reusability

Delegation has several advantages, the first one being reusability. Because the table view delegates user interaction to its delegate, the table view doesn't need to know what needs to happen when one of its rows is tapped.

Put differently, the table view can remain ignorant of the implementation details of how user interaction is handled by the application. This responsibility is delegated to the delegate, a view controller for example.

The direct benefit is that the UITableView class can be used as is in most situations. Most of the times, there's no need to subclass UITableView to adapt it to your application's needs.

Loose Coupling

Another important advantage of delegation is loose coupling. In my article about singletons, I emphasize that tight coupling should be avoided as much as possible. Delegation is a design pattern that actively promotes loose coupling. What do I mean by that?

The UITableView class is coupled to its delegate to do its work. If no delegate is associated with the table view, the table view cannot handle or respond to user interaction. This means that there needs to be a certain level of coupling. The table view and its delegate, however, are loosely coupled, because every class that implements the UITableViewDelegate protocol can act as the table view's delegate. The result is a flexible and loosely coupled object graph.

Separation of Responsibilities

A lesser known advantage of delegation is separation of responsibilities. Whenever you create an object graph, it is important to know which objects are responsible for which tasks. The delegation pattern makes this very clear.

In the case of the UITableView class, the delegate of the table view is responsible for handling user interaction. The table view itself is responsible for detecting user interaction. This is a clear separation of responsibilities. Such a separation makes your job as a developer much easier and clearer.

2. Example

There are a few flavors of the delegation pattern. Let's continue by further exploring the UITableViewDelegate protocol.

Delegation

The UITableViewDelegate protocol needs to be implemented by the table view's delegate. The table view notifies its delegate through the UITableViewDelegate protocol about user interaction, but it also uses the delegate for its layout.

An important difference between Swift and Objective-C is the possibility to mark protocol methods as optional. In Objective-C, the methods of a protocol are required by default. The methods of the UITableViewDelegate protocol, however, are optional. In other words, it is possible for a class to conform to the UITableViewDelegate protocol without implementing any of the protocol's methods.

In Swift, however, a class conforming to a particular protocol is required to implement every method defined by the protocol. This is much safer since the delegating object doesn't need to verify whether the delegate implements a protocol method. This subtle, but important, difference is illustrated later in this tutorial when we implement the delegation pattern.

Data Source

There is another pattern that is closely related to the delegation pattern, the data source pattern. The UITableViewDataSource protocol is an example of this pattern. The UITableView class exposes a dataSource property that is of type UITableViewDataSource (id<UITableViewDataSource> in Objective-C). This means that the table view's data source can be any object that implements the UITableViewDataSource protocol.

The data source object is responsible for managing the data source of the object it is the data source of. It's important to note that the data source object is responsible for keeping a reference to the items it exposes to the target object, such as a table view or collection view.

A table view, for example, asks its data source for the data it needs to display. The table view is not responsible for keeping a hold of the data objects it needs to display. That role is handed to the data source object.

The data source pattern fits nicely in the Model-View-Controller or MVC pattern. Why is that? A table view, for example, is part of the view layer. It doesn't and shouldn't know about the model layer and isn't in charge of handling the data that is coming from the model layer. This implies that the data source of a table view, or any other view component that implements the data source pattern, is often a controller of some sort. On iOS, it's usually a UIViewController subclass.

The method signatures of a data source protocol follow the same pattern as those of a delegate protocol. The object sending the messages to the data source is passed as the first argument. The data source protocol should only define methods that relate to the data that's being used by the requesting object.

A table view, for example, asks its data source for the number of sections and rows it should display. But it also notifies the data source that a row or section was inserted or deleted. The latter is important since the data source needs to update itself to reflect the changes visible in the table view. If the table view and the data source get out of sync, bad things happen.

3. Implementation

Objective-C

Implementing the delegate pattern is pretty simple now that we understand how it works. Take a look at the following Objective-C example.

We declare a class, AddItemViewController, which extends UIViewController. The class declares a property, delegate, of type id<AddItemViewControllerDelegate>. Note that the property is marked as weak, which means that an AddItemViewController instance keeps a weak reference to its delegate.

Also note that I've added a forward protocol declaration below the import statement of the UIKit framework. This is necessary to avoid a compiler warning. We could move the protocol declaration below the import statement, but I prefer to put it below the class interface. This is nothing more than a personal preference.

The protocol declaration is also pretty simple. The AddItemViewControllerDelegate protocol extends the NSObject protocol. This isn't mandatory, but it will prove to be very useful. We'll find out why that is a bit later.

The AddItemViewControllerDelegate protocol declares two required methods and one optional method. As I mentioned earlier, it's a good practice to pass the delegating object as the first parameter of every delegate method to inform the delegate which object is sending the message.

The required methods notify the delegate about an event, a cancelation or an addition. The optional method asks the delegate for feedback. It expects the delegate to return YES or NO.

This is the first piece of the delegation puzzle. We've declared a class that declares a delegate property and we've declared a delegate protocol. The second piece of the puzzle is invoking the delegate methods in the AddItemViewController class. Let's see how that works.

In the implementation of the AddItemViewController class, we implement a cancel: action. This action could be hooked up to a button in the user interface. If the user taps the button, the delegate is notified of this event and, as a result, the delegate could dismiss the AddItemViewController instance.

It is recommended to verify that the delegate object isn't nil and that it implements the delegate method we're about to invoke, viewControllerDidCancel:. This is easy thanks to the respondsToSelector: method, declared in the NSObject protocol. This is the reason why the AddItemViewControllerDelegate protocol extends the NSObject protocol. By extending the NSObject protocol, we get this functionality for free.

You can omit the check for the delegate property being nil, since respondsToSelector: will return nil if the delegate property is nil. I usually add this check since it clearly shows what we're testing.

The third and final piece of the puzzle is the implementation of the delegate protocol by the delegate object. The following code snippet shows the creation of an AddItemViewController instance and the implementation of one of the delegate methods.

Don't forget to conform the class that acts as the delegate to the AddItemViewControllerDelegate protocol as shown below. You can add this in the class interface or in a private class extension.

Swift

In Swift, the delegation pattern is just as easy to implement and you'll find that Swift makes delegation slightly more elegant. Let's implement the above example in Swift. This is what the AddItemViewController class looks like in Swift.

The protocol declaration looks a bit different in Swift. Note that the AddItemViewControllerDelegate protocol extends the NSObjectProtocol instead of the NSObject protocol. In Swift, classes and protocols cannot have the same name, which is why the NSObject protocol is named differently in Swift.

The delegate property is a variable of type AddItemViewControllerDelegate?. Note the question mark at the end of the protocol name. The delegate property is an optional.

In the cancel(_:) method, we invoke the viewControllerDidCancel(_:) delegate method. That single line shows how elegant Swift can be. We safely unwrap the delegate property before invoking the delegate method. There's no need to check if the delegate implements the viewControllerDidCancel(_:) method since every method of a protocol is required in Swift.

Let's now look at the ViewController class, which implements the AddItemViewControllerDelegate protocol. The interface shows us that the ViewController class extends the UIViewController class and adopts the AddItemViewControllerDelegate protocol.

In the addItem(_:) method, we initialize an instance of the AddItemViewController class, set its delegate property, and present it to the user. Note that we've implemented every delegate method of the AddItemViewControllerDelegate protocol. If we don't, the compiler will tell us that the ViewController class doesn't conform to the AddItemViewControllerDelegate protocol. Try this out by commenting out one of the delegate methods.

Swift Protocol Implementation Warning

Conclusion

Delegation is a pattern you'll come across frequently when developing iOS and OS X applications. Cocoa relies heavily on this design pattern so it's important to become familiar with it.

Since the introduction of blocks, a few years ago, Apple has slowly offered an alternative blocks-based API to some delegation implementations. Some developers have followed Apple's lead by offering their own blocks-based alternatives. The popular AFNetworking library, for example, relies heavily on blocks instead of delegation, resulting in an elegant, intuitive API.

Tags:

Comments

Related Articles