A delegate is a common design pattern used in Cocoa and CocoaTouch frameworks, where one class delegates responsibility for implementing some functionality to another. This follows a principle of separation of concerns, where the framework class implements generic functionality while a separate delegate instance implements the specific use case.
Another way to look into delegate pattern is in terms of object communication. Objects
often needs to talks to each other and to do so an object need conform to a protocol
in order to become a delegate of another Object. Once this setup has been done, the other object talks back to its delegates when interesting things happens.
For example, A view in userinterface to display a list of data should be responsible only for the logic of how data is displayed, not for deciding what data should be displayed.
Let's dive into a more concrete example. if you have two classes, a parent and a child:
class Parent { }
class Child { }
And you want to notify the parent of a change from the child.
In Swift, delegates are implemented using a protocol
declaration and so we will declare a protocol
which the delegate
will implement. Here delegate is the parent
object.
protocol ChildDelegate: class {
func childDidSomething()
}
The child needs to declare a property to store the reference to the delegate:
class Child {
weak var delegate: ChildDelegate?
}
Notice the variable delegate
is an optional and the protocol ChildDelegate
is marked to be only implemented by class type (without this the delegate
variable can't be declared as a weak
reference avoiding any retain cycle. This means that if the delegate
variable is no longer referenced anywhere else, it will be released). This is so the parent class only registers the delegate when it is needed and available.
Also in order to mark our delegate as weak
we must constrain our ChildDelegate protocol to reference types by adding class
keyword in protocol declaration.
In this example, when the child does something and needs to notify its parent, the child will call:
delegate?.childDidSomething()
If the delegate has been defined, the delegate will be notified that the child has done something.
The parent class will need to extend the ChildDelegate
protocol to be able to respond to its actions. This can be done directly on the parent class:
class Parent: ChildDelegate {
...
func childDidSomething() {
print("Yay!")
}
}
Or using an extension:
extension Parent: ChildDelegate {
func childDidSomething() {
print("Yay!")
}
}
The parent also needs to tell the child that it is the child's delegate:
// In the parent
let child = Child()
child.delegate = self
By default a Swift protocol
does not allow an optional function be implemented. These can only be specified if your protocol is marked with the @objc
attribute and the optional
modifier.
For example UITableView
implements the generic behavior of a table view in iOS, but the user must implement two delegate classes called UITableViewDelegate
and UITableViewDataSource
that implement how the specific cells look like and behave.
@objc public protocol UITableViewDelegate : NSObjectProtocol, UIScrollViewDelegate { // Display customization optional public func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) optional public func tableView(tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) optional public func tableView(tableView: UITableView, willDisplayFooterView view: UIView, forSection section: Int) optional public func tableView(tableView: UITableView, didEndDisplayingCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) ... }
You can implement this protocol by changing your class definition, for example:
class MyViewController : UIViewController, UITableViewDelegate
Any methods not marked optional
in the protocol definition (UITableViewDelegate
in this case) must be implemented.