Delegation in Swift

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 a delegation process in the real world.

It’s a ONE TO ONE communication pattern, which means if we are trying to communicate to/from more than ONE receiver/sender 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 needs 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 for 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()

 

Continue iOS Architecture Studying

Wouldn’t be great to know how big techs like Uber handle the dependency injection of their apps? With this article you will learn the framework that empowers hundreds of iOS developers in Uber to do dependency injection with compile time safeness.

Architecture is not to make things easier to change, but also to make things easier to reason about. Thinking in this point it is crucial to decrease the cognitive load of other developers by removing data clamps from your code. You will learn this anti-pattern and what are the consequences to your code base when trying to fix it.

 

Summary

That’s all my people, I hope you liked reading this article as much as I enjoyed writing it. 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 freelance writing! You can reach me on LinkedIn or Twitter and send me an e-mail through the contact page.

Image Credit: Wikiart

Share this post:

Related posts

Sponsor