Protocols in Swift and Objective-C are a powerful tool to decouple your code. They allow you to specify a contract between classes that consume them, but defer a concrete implementation to conformers. They allow you to segregate interfaces and invert control. One interesting aspect of protocols in Swift and Objective-C is that protocol members can be optional (
optional in Swift or
@optional in Objective-C). Unfortunately, this comes with a number of disadvantages and diminishes the robustness of your code, so it is often avoided. However, having optional members is sometimes the right conceptual model for your design. How can you design your protocols to provide optional semantics without specifying them as
The problem with
Let’s begin with Objective-C. Optional protocol methods were introduced in Objective-C 2.0 and are used heavily in Cocoa and Cocoa Touch. You implement protocols with optional methods all the time while working with UIKit, for example. But despite their prevalence, they are widely discouraged and considered a poor design. This is because you forgo compile-time checks for
@optional protocol methods in Objective-C. If a method is optional, then the compiler has no way to enforce that the conformers implement it. On the other hand, it is an error if you declare protocol conformance but fail to implement the required methods. Thus, it is the caller’s responsibility to check
-respondsToSelector: before calling an optional method. If you forget this check and the class does not implement the optional method, you’ll crash at runtime with a ‘does not respond to selector’ exception.
Consider the following example:
Calling required methods on the
delegate is straightforward:
With optional methods, not only do you forgo help from the compiler, but you incur the additional runtime cost of checking
-respondsToSelector: every time you need to message the
The problem with
Swift addresses the safety problems above and offers a convenient
? syntax for optional members:
In this case, you do not have to worry about runtime crashes in Swift, but there is another problem. In Swift,
optional is not really “part of the language” or “pure Swift” — the feature relies on the Objective-C runtime and it only exists for interoperability with Objective-C. Any protocol in Swift that contains optional members must be marked as
@objc. I have written before about avoiding
@objc in your Swift code as much as possible. When
@objc infiltrates your object graph, nearly everything must inherit from
NSObject which means you cannot use Swift structs, enums, or other nice features. This leaves you not writing Swift, but merely “Objective-C with a new syntax”. Clearly,
optional isn’t much of an option in Swift.
The ‘never optional’ solution
One naive solution is to simply never use
@optional. This is easy and straightforward. It’s great for simple cases. You provide a strict contract and avoid the shortcomings mentioned above, but in many cases this places an unnecessary burden on classes that conform to the protocol. You end up with a slew of empty methods, or methods that return sentinel values like
false. Consider the familiar
UITableViewDataSource protocol. It has two required methods and nine optional methods. Imagine if these were all
@required but you wanted to opt-out of those behaviors. You would have nine empty method stubs, or you would have to return
nil for methods like
tableView(_: titleForHeaderInSection:) -> String?.
Using multiple protocols and properties
A better approach is to split up large protocols into smaller ones, and provide a unique property (like a delegate) for each one. Again, consider
UITableViewDataSource. There are clear semantic groupings for these methods. It could easily be broken up into multiple protocols and
UITableView could have a property for each one. Ash Furrow has a great article on doing exactly that. Thus, we could reimagine these APIs in the following way:
This design transfers the “optional-ness” from the protocol itself to an additional optional property on the class. If you want headers and footers in your table view, you can opt-in to those by setting
titlesDataSource. To opt-out, you can set this property to
nil. The same applies to
reorderingDataSource, and so on. This design feels appropriate for
UITableView at first. Many of the methods are not directly related to one another and there are clear semantic groupings. In practice, however, it feels awkward having to access multiple separate properties to query the same underlying data source.
Having these disjoint protocols and properties is not desirable. Despite having nice semantic groupings, all of these methods are related in the sense that they all need access to the same underlying data in order to work properly together. To accommodate the complete
UITableViewDataSource protocol, there would be five distinct protocols, each with a corresponding property on
UITableView. Then you could reorganize the
UITableViewDelegate protocol in the same way, which would have at least 10 protocols and properties. Having this many
delegate properties is unintuitive and cumbersome. How can we improve this?
Instead of numerous disjoint protocols, you can design a union of protocols. This provides a single, top-level “entry point” to reference. You can extract the optional members of a protocol into a new protocol, then add an optional property for this new protocol on the original protocol. The result is a comprehensive top-level protocol and a set of “nested” protocols.
Adjusting the table view example above:
Now the table view has a single
dataSource property. The other protocols still exist, but they are absorbed into the
reordering properties. Another positive aspect of this design is that the opt-in/opt-out behavior for the nested protocols is explicitly declared. The conformer to
TableViewDataSource can return
nil to opt-out or return
self to opt-in to these additional methods.
Accessing these nested members goes through a single point of access:
This reduces the API surface area of
UITableView by only having a single
dataSource property instead of five — not to mention the 10 potential
delegate properties there could have been after splitting up
UITableViewDelegate. It unifies all of the methods of the data source protocol without resorting to using
optional, while allowing you to opt out of the additional behavior in a concise way. In the case of Objective-C, the check for
-respondsToSelector: becomes a simple check for
nil instead, and the compiler can enforce that the entire protocol is implemented. Overall, it feels cleaner and much more cohesive, especially at the call site.
See the full gist for more details.
As we’ve explored, there are a number of ways to design a solution to the “optional protocol problem”. You can design a model that avoids optionality altogether, you can provide many protocols with corresponding properties, or you can design a “nested composition” of protocols. Every situation is different, but I often find this nested composition approach to be the most elegant, powerful, and intuitive.