Communication Patterns Series - The Swift Delegation
Hello folks, Leo here.
Today we'll discuss the most famous communication pattern in Swift/iOS development: the delegation pattern.
The delegation pattern is very simple if we try to imagine it is just something delegating to other thing.
It's a ONE TO ONE communication pattern, that means if we are trying to communicate to/from more than ONE receivers/senders this isn't the best way to do that.
Example: Imagine that you are hungry and have a friend that went out to buy some food. You can ask (delegate) him to bring you food.
In swift we can do the same.
If we have a Screen A that need data from Screen B, we can simply create a delegation communication pattern between them.
Let's go to the code!
Example: Imagine that we have a CardView's data that we need in the HomeViewController. The full code example is the end of the post.
1 - First, create a protocol in which the views will communicate. This protocol says: "Well I'm waiting some input from somewhere".
protocol HomeViewControllerDelegate: class {
func setDataReceivedFromCard(data: String)
}
class HomeViewController: ViewController {
// the HomeViewController Configuration
...
}
2 - Conform to the protocol HomeViewControllerDelegate.
extension HomeViewController: HomeViewControllerDelegate {
func setDataReceivedFromCard(data: String) {
print("data is back to the previous view")
dataLabel.text = data // imagine that we have to set the data on a label
}
}
3 - Now, in CardView, we'll have to set how we will send the HomeViewController to CardView. You have two basic ways: first one is using the init method and the second one is using a func. Remember that to avoid RETAINING CICLES the delegate variable should aways be WEAK.
weak private var delegate: MyViewControllerDelegate?
// we can set the delegate here
init(delegate: MyViewControllerDelegate?) {
super.init(frame: .zero)
self.delegate = delegate
}
//OR
// we can use a func to the delegate
func set(delegate: MyViewControllerDelegate) {
self.delegate = delegate
}
4 - In CardView call the delegate var method with the data we want to send back.
@objc func sendDataToOtherViewTapped() { // in this example this is an action triggered by a button
print ("send data to other view")
delegate?.setDataReceivedFromCard(data: "Data from card is: Apple")
}
5 - Back to HomeViewController, don't forget to set the self to the CardView when configuring UI / or whatever fits for you
func configureCardView() {
view.addSubview(cardView)
cardView.translatesAutoresizingMaskIntoConstraints = false
cardView.backgroundColor = .black
cardView.set(delegate: self) // DON'T FORGET THIS!!!!!
}
That's all folks!
The full code example below can be executed in Swift Playground:
import UIKit
import PlaygroundSupport
protocol HomeViewControllerDelegate: class {
func setDataReceivedFromCard(data: String)
}
class HomeViewController : UIViewController {
let cardView = Card()
let animationButton = UIButton(type: .system)
let dataLabel = UILabel()
var cardShow = false
override func loadView() {
let view = UIView()
view.backgroundColor = .white
self.view = view
}
override func viewDidLoad() {
super.viewDidLoad()
configureAnimationButton()
configureCardView()
}
func configureCardView() {
view.addSubview(cardView)
cardView.translatesAutoresizingMaskIntoConstraints = false
cardView.backgroundColor = .black
cardView.set(delegate: self)
view.addSubview(dataLabel)
dataLabel.translatesAutoresizingMaskIntoConstraints = false
dataLabel.font = UIFont.systemFont(ofSize: 20, weight: .bold)
NSLayoutConstraint.activate([
cardView.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.5),
cardView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.9),
cardView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
cardView.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: 1000),
dataLabel.topAnchor.constraint(equalTo: animationButton.bottomAnchor, constant: 15),
dataLabel.heightAnchor.constraint(equalToConstant: 30),
dataLabel.centerXAnchor.constraint(equalTo: animationButton.centerXAnchor)
])
}
func configureAnimationButton() {
view.addSubview(animationButton)
animationButton.translatesAutoresizingMaskIntoConstraints = false
animationButton.setTitle("Animate", for: .normal)
animationButton.layer.cornerRadius = 10
animationButton.backgroundColor = .systemGreen
animationButton.setTitleColor(.label, for: .normal)
animationButton.titleLabel?.font = .systemFont(ofSize: 36, weight: .bold)
animationButton.addTarget(self, action: #selector(animateButton), for: .touchUpInside)
print("será?")
NSLayoutConstraint.activate([
animationButton.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor,
constant: 20),
animationButton.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
animationButton.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
animationButton.heightAnchor.constraint(equalToConstant: 50)
])
}
@objc func animateButton() {
if !cardShow {
UIView.animate(withDuration: 0.2) { [weak self] in
self?.cardView.transform = CGAffineTransform(translationX: -1000, y: 0)
}
} else {
UIView.animate(withDuration: 0.4) { [weak self] in
self?.cardView.transform = CGAffineTransform(translationX: 1000, y: 0)
}
}
cardShow.toggle()
}
}
extension HomeViewController: HomeViewControllerDelegate {
func setDataReceivedFromCard(data: String) {
print("data is back to the previous view")
dataLabel.text = data
}
}
class Card: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
configureUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
weak private var delegate: HomeViewControllerDelegate?
// you can set the delegate here
init(delegate: HomeViewControllerDelegate?) {
super.init(frame: .zero)
self.delegate = delegate
}
//OR
// you can use a func to the delegate
func set(delegate: HomeViewControllerDelegate) {
self.delegate = delegate
}
func configureUI() {
let sendDataToSuperViewButton = UIButton(type: .roundedRect)
addSubview(sendDataToSuperViewButton)
sendDataToSuperViewButton.translatesAutoresizingMaskIntoConstraints = false
sendDataToSuperViewButton.backgroundColor = .systemBlue
sendDataToSuperViewButton.setTitle("Send data to Super view", for: .normal)
sendDataToSuperViewButton.setTitleColor(.black, for: .normal)
sendDataToSuperViewButton.addTarget(self, action: #selector(sendDataToOtherViewTapped), for: .touchUpInside)
sendDataToSuperViewButton.layer.cornerRadius = 10
NSLayoutConstraint.activate([
sendDataToSuperViewButton.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -15),
sendDataToSuperViewButton.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 15),
sendDataToSuperViewButton.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -15),
sendDataToSuperViewButton.heightAnchor.constraint(equalToConstant: 50)
])
}
@objc func sendDataToOtherViewTapped() {
print("send data to other view")
delegate?.setDataReceivedFromCard(data: "Data from card is: Apple")
}
}
PlaygroundPage.current.liveView = HomeViewController()
Summary
That's all my people, I hope you liked as I enjoyed write this article. If you want to support this blog you can Buy Me a Coffee or just leave a comment saying hello. You can also sponsor posts and I'm open to writing freelancing
! Just reach me in LinkedIn or Twitter for details.
Thanks for the reading and... That's all folks.
Did you find this article valuable?
Support Leonardo Maia Pugliese by becoming a sponsor. Any amount is appreciated!