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.
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) {
}
}
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)
}
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)
}
}
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
}
}
}
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)