The Delegate Pattern, also referred to as Delegation, is an architectural pattern frequently found in Swift code and Apple libraries. This post presents an overview of Delegation and an example of creating a custom delegate in Swift:
- The Delegate Pattern in Swift
- Create A Delegate Protocol
- Extend A Delegate Protocol
- Implement A Custom Delegate
- How To Use A Custom Delegate
- Creating Optional Delegate Methods
The Delegate Pattern in Swift
The name Delegate comes from the concept of delegating control (giving control to something else). In Swift, a delegate is a controller object with a defined interface that can be used to control or modify the behavior of another object.
One example is the UIApplicaitonDelegate
in an iOS app. The iOS system uses the UIApplicationDelegate
to modify the behavior of iOS, like receiving a push notification, opening a URL
, and launching your application.
Create A Delegate Protocol
The first step to create a custom delegate is to create a delegate protocol. A protocol is an interface, a set of variables and functions, that the custom delegate must implement.
// Define a custom protocol called ViewActionDelegate
protocol ViewActionDelegate {
// Define expected delegate variables
var state: ViewState { get }
var userID: String? { get set }
// Define expected delegate blocks
var errorHandler: ((Error) -> Void)? { get set }
// Define expected delegate functions
func handle(action: ViewAction)
}
In the definition of ViewActionDelegate
, and in the following code examples, the enums ViewState
and ViewAction
are used:
enum ViewState {
case `default`
case loading
}
enum ViewAction {
case save
case cancel
}
Extend A Delegate Protocol
A delegate, like other Swift types, can be extended to add computed variables and functions.
// Extend ViewActionDelegate
extension ViewActionDelegate {
// Define a computed variable
var isReadyForNextAction: Bool {
return state != .loading
}
}
Implement A Custom Delegate
The next step is to create a class or struct that adheres to the custom delegate ViewActionDelegate
. A clean approach to doing so is to first define a class
or struct
, and then extend the class
or struct
to adhere to the delegate.
// Define a class ActionController
class ActionController {
// Define variables that match the delegate
// protocol definition
var state: ViewState = .default
var userID: String?
var errorHandler: ((Error) -> Void)?
}
// Extend ActionController to adhere to the delegate
extension ActionController: ViewActionDelegate {
// Implement the functions required by the delegate
// protocol definition
func handle(action: ViewAction) {
switch action {
case .save:
break
case .cancel:
break
}
}
}
How To Use A Custom Delegate
Finally, the last step is to use the custom delegate. For this example, a UIView
called ActionView
is created that uses a ViewActionDelegate
to modify behavior.
The following example uses an optional delegate reference in ActionView
, and optional references are weak
(in terms of memory management). To learn more about memory management related to delegates, check out Examples of Strong, Weak, and Unowned References in Swift.
class ActionView: UIView {
// Create a optional variable to maintain a reference to
// the delegate
var delegate: ViewActionDelegate?
// Modify the behavior of save based on the delegate’s
// implementation of handle(action:)
@IBAction func save() {
guard let delegate = delegate else { return }
guard delegate.isReadyForNextAction else { return }
delegate.handle(action: .save)
}
// Modify the behavior of handle(error:) based on the
// delegate’s errorHandler
func handle(error: Error) {
delegate?.errorHandler?(error)
}
}
The implementation of a custom delegate can be completed by using the delegate
property of the ActionView
to modify behavior. Here, an ActionController
is customized and assigned to the delegate
property of an ActionView
.
class ActionViewController: UIViewController {
var actionView = ActionView()
var actionController = ActionController()
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// Configure the actionController
actionController.userID = "@robert"
actionController.errorHandler = { (error) in
// Handle error
}
// Assign the actionController as the delegate
// of actionView
actionView.delegate = actionController
}
}
Creating Optional Delegate Methods
Objective-C delegates can have optional methods. In Swift, using the @objc
modifier a protocol can be marked as an Objective-C protocol. After marking the protocol as @objc
, specific protocol functions can be marked with @objc
which allows the optional
modifier to be used on the marked function.
// Define an @objc delegate in Swift
@objc protocol ViewActionDelegate {
// Create an optional delegate function
@objc optional func ignoreActions() -> Bool
}
Optional delegate methods can then be implemented and called in your application:
if delegate.ignoreActions?() == true {
// Handle case
}
Implementing A Custom Delegate in Swift
That’s it! By using delegation and creating a custom delegate in Swift you can customize the behavior of objects and better manage complexity in your app.