DEV Community

mrcflorian
mrcflorian

Posted on • Edited on

Delegation Pattern in Swift by Example

While working on several iOS App Templates, I realized how widely spread is the delegation pattern in Swift. That's extremely obvious even in Apple's frameworks. UITableView & UICollectionView are probably the most common examples of delegation in Swift. In this article, we are taking a closer look at what is the delegate pattern and how do we implement it in Swift.

alt delegation swift

The delegation pattern is a messaging design pattern in Swift, that's being used for 1-1 communication between objects. It leverages Swift protocols to avoid coupling components together.

Let's take a common example by creating a simple view such as:

class BubbleView: UIView {
    override init(frame: CGRect) {
        super.init(frame: frame)
        setup()
    }
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setup()
    }
    private func setup() {
        self.isUserInteractionEnabled = true
        let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(BubbleView.didTapIntoButton))
        self.addGestureRecognizer(tapGestureRecognizer)
    }

    @objc func didTapIntoButton(_ sender: UITapGestureRecognizer) {
    }
}
Enter fullscreen mode Exit fullscreen mode

We have a class named BubbleView. Let’s define a delegate, by writing it as a protocol:

protocol BubbleViewDelegate: class {
    func userDidTap(into bubbleView: BubbleView)
}
Enter fullscreen mode Exit fullscreen mode

Observe how we are conforming to Apple's guidelines on naming the delegate methods (we include the caller as well as "didDo" format).

Going back to our view class, let's add a new property:

class BubbleView: UIView {
    { ... }
    weak var delegate: BubbleViewDelegate?

    @objc func didTapIntoButton(_ sender: UITapGestureRecognizer) {
        delegate?.userDidTap(into: self)
    }
}
Enter fullscreen mode Exit fullscreen mode

Notice how we used "weak" for the new property. This is to avoid a potential retain cycle, which will be more visible in the next step.

Let's assume we want the view controller that owns the bubble view to handle all the user actions, such as tapping on the bubble view. This is to preserve a clean MVC architecture, by separating the view layer from the controller layer.

class ContainerViewController: UIViewController {

    lazy var bubbleView: BubbleView = {
        let bubbleView = BubbleView(frame: CGRect(x: 90, y: 10, width: 200, height: 200))
        bubbleView.backgroundColor = .black
        bubbleView.layer.cornerRadius = 100
        bubbleView.delegate = self
        return bubbleView
    }()
    override func loadView() {
        super.loadView()
        view.addSubview(bubbleView)
    }
}
extension ContainerViewController: BubbleViewDelegate {
    func userDidTap(into bubbleView: BubbleView) {
        let currentBounds = view.bounds
        UIView.animate(withDuration: 1.5) {
            var frame = bubbleView.frame
            frame.origin.y = currentBounds.height
            bubbleView.frame = frame
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

That's it. We just showcased a widely spread delegation example - handling user touch events on a view, by delegating the important logic to the controller layer, in this way preserving a perfect MVC design. For the React Native version, check out instamobile.io.

Top comments (0)