Swift: Delegate and Protocol

Vu Nguyen
6 min readNov 18, 2018

Been hanging around with Swift for nearly 10 days for the upcoming project. Everything works just fine until something called Delegate and Protocol came in hitting my face really hard. That is why this note is born to uncover the underlying principle for later callback. As the article is intended for personal use and note, this will be pretty dense so please feel free to ask me for the detailed explanation.

1. What are Delegate and Protocol? Simply put, this is a design pattern in iOS application development for handling passing data from one view controller to another. Period.

2. Why on Earth do we need Delegate and Protocol? This is a really huge question. Let’s first go over the traditional approach of passing data in Swift to grab the big picture. Setup two segues for both first view controller and second view controller:

First view controller:

// ViewController.swift
// Segues
//
// Created by VuNguyen on 2018/11/17.
// Copyright © 2018 VuNguyen. All rights reserved.
//
import UIKit
class ViewController: UIViewController { var textFromSecondView : String?
@IBOutlet weak var firstLabel: UILabel!
@IBOutlet var textField: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
firstLabel.text = textFromSecondView
}
@IBAction func sendDataForward(_ sender: UIButton) {
performSegue(withIdentifier: "goToSecondScreen", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if segue.identifier == "goToSecondScreen" {
let destinationVC = segue.destination as! SecondViewController
destinationVC.textFromFirstView = textField.text!
}
}
}
ViewController’s UI

Second view controller:

//
// SecondViewController.swift
// Segues
//
// Created by VuNguyen on 2018/11/17.
// Copyright © 2018 VuNguyen. All rights reserved.
//
import UIKit
class SecondViewController: UIViewController { var textFromFirstView : String?
@IBOutlet weak var secondLabel: UILabel!
@IBOutlet weak var textField: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
secondLabel.text = textFromFirstView }
@IBAction func sendDataBackward(_ sender: UIButton) {
performSegue(withIdentifier: "goToFirstScreen", sender: self) }
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goToFirstScreen" {
let destinationVC = segue.destination as! ViewController
destinationVC.textFromSecondView = textField.text!
}
}
}
SecondViewController’s UI

You can try playing around a little bit by passing data back and forth and everything seems fine. Now here is the interesting part, let’s change the background of the first view by creating ChangeColor button:

ChangeColor button added

Send data forward to the second view:

Now send data backward:

Booommm! Where is the solid blue background? Where did it go? What happened?

The key lies in the segue.destination. What it means is that every single time we pass the data either forward or backward, A NEW instance (new object) of each view IS CREATED. So just imagine when we first enter the first screen, the default background color is gray, right? That’s the very first object of the first view controller (original first view object version). Then we hit `ChangeColor` button, which alters the property of the first object’s background color property to `blue`. In other words, the first object is updated (altered first view object version).

The second view object is created as forward data is passed. Now going backward, another first view object is newly created with the default background color property, which is gray and similar to the original first view object.

So the big question is how we could keep the first view object unchanged? How can we reuse the first view object without creating a new one?

That’s when Delegate and Protocol came to rescue. Let’s go over the definition of the concepts:

A Delegate is thing/person which is delegated or given a right by a boss/higher-up to do something. A Protocol is a contract that the Delegate must comply with.

My Swift-oriented definition:

An object (The Delegate) will comply with the rule (Protocol) established by another object (The boss). In our case, the second view object (The boss) will now own a delegate property which is the first object and the first object will comply with the rule (The Protocol) which is set by the second object.

That way, when sending the data back, we can target the right first object, the altered version with the blue background without creating a new instance. Enough talking, here is our Delegate-Protocol modified version:

First view controller:

//
// ViewController.swift
// Segues
//
// Created by VuNguyen on 2018/11/17.
// Copyright © 2018 VuNguyen. All rights reserved.
//
import UIKit
class ViewController: UIViewController, SecondViewContract {
var textFromSecondView : String?
@IBOutlet weak var firstLabel: UILabel!
@IBOutlet var textField: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
if let textFromSecondView = textFromSecondView {
firstLabel.text = textFromSecondView
}
}
@IBAction func changeColorPressed(_ sender: UIButton) {
view.backgroundColor = UIColor.blue
}
@IBAction func sendDataForward(_ sender: UIButton) {
performSegue(withIdentifier: "goToSecondScreen", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goToSecondScreen" {
let destinationVC = segue.destination as! SecondViewController
destinationVC.textFromFirstView = textField.text!
destinationVC.delegate = self
}
}
func changeFirstLabel(input: String) {
firstLabel.text = input
}

}

Second view controller:

//
// SecondViewController.swift
// Segues
//
// Created by VuNguyen on 2018/11/17.
// Copyright © 2018 VuNguyen. All rights reserved.
//
import UIKit
protocol SecondViewContract {
func changeFirstLabel(input: String)
}
class SecondViewController: UIViewController { var textFromFirstView : String?
var delegate : SecondViewContract?
@IBOutlet weak var secondLabel: UILabel!
@IBOutlet weak var textField: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
if let textFromFirstView = textFromFirstView {
secondLabel.text = textFromFirstView
}
}
@IBAction func sendDataBackward(_ sender: UIButton) {
delegate?.changeFirstLabel(input: textField.text!)
self.dismiss(animated: true, completion: nil)

}
}

Now let me start over. The SecondViewController now is the boss (Delegator if you want fancy term) who set up the rule protocol SecondViewContract. Specifically, the delegate must comply with the rule `changeFirstLabel`. The protocol is totally separate from the class SecondViewController and you are free to put in a distinct file. Here is how the magic works:

  • Step1: When the iPhone loads our first view, it creates an instance of ViewController, which is the very first view object.
  • Step2: A new instance of SecondViewController is created at the line segue.destination
  • Step3: Set new instance’s textFromFirstView property to user textfield input as usual
  • Step4: attach the first view object to the newly created second view object as the delegate
  • Step5: Data sent forward to the second view controller. In fact, data is sent to the instance in Step2 we just mentioned.
  • Step6: Because the instance in Step2 now possesses the first view object as a delegate, so it can send back the data to that first view object without creating any new instance of ViewController. Hence in the SecondViewController, we have already removed the segue.destination and call the delegate?.changeFirstLabel(textField.text!), which means “hey the very first object, please change your first label and remain your color background where it is before you sent the data here”. That’s why when we go back, the color background will be what it is before the data being sent forward.

Here is the final show:

Change color in the first view controller

Send forward:

Backward:

The solid blue color remains blue when the data was passed backward

Volla! That’s what the Delegate and Protocol are all about.

--

--