Leonardo Maia Pugliese
Holy Swift

Holy Swift

Communication Patterns Series - The Swift Delegation

Leonardo Maia Pugliese's photo
Leonardo Maia Pugliese
·May 22, 2020·

4 min read

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

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!

Learn more about Hashnode Sponsors
 
Share this