Communication Patterns Series - The Swift Delegation

Subscribe to my newsletter and never miss my upcoming articles

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()

No Comments Yet