The Delegation Pattern in Swift

The delegate pattern is a cornerstone of iOS development in UIKit, enabling a class to delegate specific tasks to another object. This pattern promotes clean and modular code by adhering to the single responsibility principle, making your apps easier to maintain and extend. If you’ve ever worked with UITableView, UICollectionView, or UITextField, you’ve already encountered this powerful design pattern.

In this blog, we’ll explore the delegate pattern in-depth, understand its advantages, and walk through an example implementation.

What Is the Delegate Pattern?

The delegate pattern is a behavioral design pattern where one object, the delegator, passes off responsibility for a specific piece of functionality to another object, the delegate. The delegator typically defines a protocol (a contract) that the delegate must conform to. This ensures that the delegate object provides the expected methods or properties.

Here’s a conceptual breakdown:

  1. Delegator: The object that needs help performing a task or notifying an event.
  2. Protocol: Defines the methods or properties that the delegate must implement.
  3. Delegate: The object that implements the protocol and performs the delegated tasks.

Why Use the Delegate Pattern?

The delegate pattern is widely used in UIKit because of the following advantages:

  • Loose Coupling: Delegators don’t need to know about the specific class of their delegate, just that it conforms to the protocol.
  • Flexibility: The delegator can work with any delegate that adheres to the protocol.
  • Reusability: The same delegator class can be used in different contexts with different delegates.
  • Clear Communication: The protocol ensures well-defined communication between objects.

Common Examples in UIKit

UIKit uses the delegate pattern extensively. Here are some common examples:

  • UITableView and UICollectionView: Use the UITableViewDelegate and UICollectionViewDelegate protocols to manage user interactions and behaviors.
  • UITextField and UITextView: Use the UITextFieldDelegate and UITextViewDelegate protocols to handle text input events.
  • UIScrollView: Uses UIScrollViewDelegate to monitor scrolling behavior.

For instance, when you create a UITableView, you implement the UITableViewDelegate protocol to respond to user interactions, such as row selection.

Implementing the Delegate Pattern in UIKit

Let’s walk through a simple implementation of the delegate pattern. Suppose we have a custom view ColorPickerView, and we want to notify a delegate whenever the user selects a color.

1. Define the Protocol

Define the protocol that specifies the methods the delegate should implement:

Swift
protocol ColorPickerDelegate: AnyObject {
    func didPickColor(_ color: UIColor)
}

Why AnyObject?
The weak keyword (used later) requires the delegate to be a class, and AnyObject ensures that the protocol is only adoptable by class types.

2. Create the Delegator Class

Define the ColorPickerView that includes a delegate property:

Swift
import UIKit

class ColorPickerView: UIView {
    // The delegate property
    weak var delegate: ColorPickerDelegate?
    private let colors: [UIColor] = [.red, .blue, .green, .yellow]
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        setupView()
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        setupView()
    }
    
    private func setupView() {
        let stackView = UIStackView(frame: self.bounds)
        stackView.axis = .horizontal
        stackView.distribution = .fillEqually
        
        // Create buttons for each color
        for color in colors {
            let button = UIButton()
            button.backgroundColor = color
            button.addTarget(self, action: #selector(colorSelected(_:)), for: .touchUpInside)
            stackView.addArrangedSubview(button)
        }
        
        self.addSubview(stackView)
    }

    @objc private func colorSelected(_ sender: UIButton) {
        guard let color = sender.backgroundColor else { return }
        // Notify the delegate
        delegate?.didPickColor(color)
    }
}

In this class:

  • The delegate is a weak reference to avoid retain cycles.
  • The didPickColor(_:) method is called when a color is selected, and it informs the delegate.

3. Implement the Delegate

In your view controller, adopt the ColorPickerDelegate protocol and set the delegate property of ColorPickerView:

Swift
import UIKit

class ViewController: UIViewController, ColorPickerDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()
        let colorPicker = ColorPickerView(frame: CGRect(
                x: 0, 
                y: 100, 
                width: self.view.bounds.width, 
                height: 50
            )
        )
        colorPicker.delegate = self
        self.view.addSubview(colorPicker)
    }
    
    // Conform to the protocol
    
    func didPickColor(_ color: UIColor) {
        self.view.backgroundColor = color
    }
}

Here:

  • The ViewController implements the ColorPickerDelegate protocol.
  • The didPickColor(_:) method updates the background color of the view when a color is selected.

4. Test the Implementation

When you run the app and select a color in the ColorPickerView, the didPickColor(_:) method in the ViewController is called, changing the background color of the screen.

Best Practices

  • Use weak for Delegates: Always declare delegate properties as weak to prevent retain cycles.
  • Document Protocols Clearly: Clearly document the purpose of each method in the protocol for better maintainability.
  • Prefer Protocols Over Subclassing: When adding functionality to a class, use the delegate pattern instead of subclassing for better flexibility.

Conclusion

The delegate pattern is a powerful tool in UIKit, enabling smooth communication between objects while maintaining loose coupling. By defining clear protocols, you can create reusable and modular components that integrate seamlessly into your app. Whether you’re building a simple custom view or working with UIKit’s prebuilt components, mastering the delegate pattern will make you a more proficient iOS developer.

Try incorporating the delegate pattern in your next project and see how it simplifies your codebase!


Posted

in

,