Delegate in Swift

Dmytro Anokhin
7 min readSep 12, 2018

--

When discussing a delegate I often see comparison with notifications, observing, and callbacks (closures or blocks). While solving many common problems this concepts are different. This article describes the delegation concept, how to implement proper delegate, best practices, and compares the delegate pattern with other approaches.

I prefer to separate delegation as a concept described in the “Gang of Four” book and the delegate pattern in Cocoa/Cocoa Touch.

The Delegation Concept

Delegate (verb) — entrust (a task or responsibility) to another person.

New Oxford American Dictionary

In programming terms this quote translates to entrust (a task or responsibility) to another object.

The delegation uses objects composition to allow customization and code reuse, and stands as an alternative to inheritance. Relationship between a delegating object and its delegate is an analogy to how a subclass forward methods to its superclass.

The difference that stands out is that inheritance is static and composition is dynamic. Using delegation allows changing objects behaviour at runtime.

Here I have an example hierarchy of objects. ShapeView is an abstract class that provides stroke and fill colors. RectangleView and EllipseView implement drawing logic by overriding draw(_:) method.

This is a valid use of inheritance. However delegation brings certain benefits to the table. In this example ShapeView uses the delegate to hand over drawing to another object. This allows customizing drawing routine at runtime without the need to reinstantiate the view.

A method can refer to the current instance using self keyword. Delegating object should pass itself to the delegate as the first argument: func drawShapeView(_ shapeView: ShapeView). This allows a delegate to refer back or differentiate between different objects if it stands as a delegate of multiple objects.

This is probably the only requirement the delegation concept impose. Implementation details rather depend on programming language and framework.

C# for instance has the System.Delegate class and delegate keyword.

Nor Swift or Objective-C has this. Instead we have conveniences on how to implement the delegate pattern.

The Delegate Pattern

The delegate pattern is very common in Cocoa and Cocoa Touch frameworks. Delegation used to notify about an event that will or was handled. Sometimes delegates can alter the impending event or block it completely.

Classic implementation of delegate in Objective-C looks like this:

This is the analogy in Swift:

Confirming to protocol in Objective-C is convenient using class extensions:

Using #pragma mark - MyDelegate or // MARK: - MyDelegate creates visual separation in the editor and the jump bar.

In Swift it is convenient to implement protocols in extensions.

Protocol Declaration

@protocol Delegate <NSObject>protocol Delegate : AnyObject

A protocol defines a blueprint of methods, properties, and other requirements that suit a particular task or piece of functionality.

The Swift Programming Language

Protocols are perfect fit for the delegation pattern because they provide right amount of control over requirements, not more or less than needed. This way a delegating object is not aware of implementation details of a delegate.

The Delegate protocol inherits the NSObject protocol in order to provide introspection for optional methods.

In Swift the Delegate declared as a class-only protocol to use it as a weak reference.

Alternative declaration is using informal protocols, typically categories on NSObject, but this approach considered to be legacy and is not used in modern frameworks.

Property and Memory Management

@property (nonatomic, weak) id<Delegate> delegate;weak var delegate: Delegate?

Property in Objective-C is declared as id<Delegate>:

  • id is a pointer to any object, delegates are not limited to NSObject subclasses;
  • Delegate inherits NSObject protocol to provide necessary runtime methods.

To prevent strong reference cycles, delegate properties are declared weak. In Swift this is provided by using class-only protocols.

Why do we want to declare delegates as weak references? Consider MVC pattern where a controller is a delegate of a view. Both instances hold a reference to each other. This can create strong reference cycle, which keeps a view and a controller alive. We need a weak reference to resolve this.

Optionals

Delegates can have optional methods. In Objective-C group of optional methods is marked with @optional keyword.

Before calling optional method we must ensure that method is implemented. In Objective-C we can use respondsToSelector: because we inherit NSObject protocol.

Swift has optional modifier for interoperability with Objective-C. We must use@objc for both protocol and optional method. This also requires arguments to be representable in Objective-C.

Overall declaration looks quite cumbersome. Calling optional method in Swift is convenient using optional chaining.

More Swifty was can be using protocol extensions to replace optional methods with default, empty implementations.

Naming Conventions

Delegate protocols are typically tightly connected with class declaration, without a class there is no use for a delegate. Nor Swift or Objective-C allows nested protocol declarations. Therefore the name of a delegate protocol must start with the class name.

Method names start with the class name (without a prefix) and first argument must be the delegating object itself.

This allows to refer back and useful when we want to provide default value or handle multiple delegating objects.

Method names should include auxiliary verbs (should, will, has, did) to identify a transpired or impending event.

Exceptions are common for cases where a delegate alters the impending event or provides additional information.

Delegate and Data Source

A data source is like a delegate except that, instead of being delegated control of the user interface, it is delegated control of data.

Concepts in Objective-C Programming

The Concepts in Objective-C Programming guide is a bit outdated and nowdays delegation obviously used to handle events not only in the user interface.

Data sources follow the same conventions as delegates. Except when all methods in a delegate can be optional, a data source contains required methods (data source that does not provide data it not very useful 😊) .

Some objects in this data sources are very heavy, like UICollectionViewCell, and UIViewController. Using this pattern allows consumers to lazily query providers and reuse allocated instances.

Also separating objects into handling events and providing data makes logical sense.

Delegate and Inheritance

Subclassing a class that has a delegate can reveal some issues. Take look at inheritance hierarchy of theUITableView.

This design is actually not correct. Consider this code.

UITableView expects its delegate to be UITableViewDelegate type. However we can provide the UIScrollViewDelegate subtype. This breaks the substitutability principle: return value can be subtype, argument can not be supertype:

You can read more about Liskov Substitution Principle and Covariance and Contravariance by Mike Ash on the topic.

This code compiles and works of two reasons:

  • It compiles because the code is imported from Objective-C. Swift won’t allow this kind of overriding;
  • It doesn’t break at runtime because all methods in delegate are optional and implementation checks if an instance implements a method. This is not the case for required methods in a data source.

Clang has -Wincompatible-property-type diagnostic flag resulting in warning in Objective-C:

property type A is incompatible with type B inherited from C

Swift has a diagnostics error override_property_type_mismatch:

property ‘A’ with type ‘B’ cannot override a property with type ‘C’

So what to do when we need to add methods to a delegate?

The way is to create a separate delegate. The UITableView actually has multiple delegates for specific features:

Alternatively, if you’re not subclassing, you can inherit protocol to implement separation of concerns. URLSession is a good example. The delegate has URLSessionDelegate type that stands as a base class for rich hierarchy including delegates for data, download, and stream tasks.

View Controllers Presentation

Delegates are useful when it comes to view controllers presentation. Edit view controller can notify the delegate about completion or cancellation of its intent.

Presenting view controller sets itself as a delegate of a presented view controller. Upon receiving a callback it can reflect changes and dismiss a presented controller. This is a nice way to communicate back changes and it doesn’t depend on concrete presentation.

Similar Patterns

While delegation concept describes how to replace inheritance with composition, the delegate pattern in Cocoa and Cocoa Touch is used primary as an event handling mechanism.

Target-Action

Target-action has a very specific use in Cocoa and Cocoa Touch — handling events from NSControl and UIControl.

How it compares to a delegate? Target-action is more strict, its API requires to action (selector) to have specific signature. There is also no way to alter or block the impending event.

Target-action is provided by a base class. It supports multiple targets and is more dynamic. For example, when no target provided, a control can determine the target at runtime using responder chain.

Notifications

Notifications is communication mechanism. It doesn’t create explicit connection between instances, but acts through shared instance (a notification center). Notifications allow one to multiple connections and provide no way to alter or block the impending event.

Closures as an Alternative

Closures are probably closest alternative for delegation. We can replace the delegate in the ShapeView with closures.

Closures are one to one relationship but it is simpler to make them one to many without touching Objective-C runtime.

Because a closure can have return value they can alter or block the impending event. This also makes closures alternative for a data source.

Closures provide better support for required and optional routines. With protocols we must use optional and @objc attributes. This limits abilities to use Swift types, requires an object implementing protocol to be Objective-C class, and uses Objective-C messaging. With closures the difference between required and optional is difference between non-optional and optional type.

Closures can be provided inline. This can bring benefit of having code “inline” but can lead to cluttering function scope when many closures need to be implemented.

Overall closures are Swiftier alternative to the delegate pattern.

Delegation is intensively used in Cocoa and Cocoa Touch frameworks and it is important to know it.

As you can see, it doesn’t take much to implement proper delegate pattern. Pay attention to memory management and test for optional methods. Following naming conventions will make your delegates look professional. And if you’re looking for and alternative to delegation closures are the best choice.

Thank you for reading. If you like the article, please follow me and share.

Show authors more ❤️ with 👏’s

And hope to see you next time 😊

--

--

Dmytro Anokhin
Dmytro Anokhin

Written by Dmytro Anokhin

iOS Developer, here to share best practices learned through my experience. You can find me on Twitter: https://twitter.com/dmytroanokhin

Responses (1)