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:

  1. The Delegate Pattern in Swift
  2. Create A Delegate Protocol
  3. Extend A Delegate Protocol
  4. Implement A Custom Delegate
  5. How To Use A Custom Delegate
  6. 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.