Delegate in Swift

Delegation is a design pattern that enables a class or structure to hand off (or delegate) some of its responsibilities to an instance of another type.

Conversely speaking Delegate is nothing but a class/structure that does some work for another class or structure.

Advantages of using Delegate
  • Delegate helps in keeping Class/Struct clean. Class can concentrate on its main purpose and leave the tasks which its not supposed to do for other Classes.
  • Using Delegate we can leave the scope of customisation to others. iOS heavily does this. For example UITableView leaves the tasks to define the number of rows, data for each row, action when row is clicked etc for its delegates.
  • Delegate is also used as one to one message passing system. If you want to notify Class A of some event that is happening on Class B, you can do this via Delegate. Declare one delegate variable in Class B, assign its value to be an instance of Class A, when some event happens on Class B you can say that inform my delegate( instance of Class A in this case) about this event. Upon receiving this message instance of Class A can take whatever action it wants.

iOS heavily uses Delegate pattern, for both customisation and message passing(notifications). In iOS Delegate is implemented with Protocol. You will find many protocols, UITableViewDelegate, UITableViewDataSource, URLSessionDataTask, UICollectionViewDelegate to name a few. You should appreciate how beautifully classes are implemented and leave the scope for customisation and responding to events to its delegates.

Defining your own custom Delegate is also very simple is iOS using Swift.
Suppose you want to implement following.

DelegatVid

What is happening here is, there is a main view, when you click on Change Background Color, color picker is presented, on clicking one of the colors the background color of main view is changed and Color picker gets dismissed.
The challenge here is that main view should somehow know that a button is clicked and which button is clicked.
We can implemented this via Delegate.
Download the Starter Project from HERE.
You will find everything set in this project. Go ahead and run the project. You will be able to launch the Color Picker by clicking on Change Background Color button. Try tapping on one of the colors. Nothing will happen. This is the missing link. The actions are defined for the button clicks. But we would like to pass this event along with color to the main(presenting) view. We will use Delegate for this.

Open ColorPickerViewController.
Add following lines just above class ColorPickerViewController: UIViewController {

protocol ColorPickerDelegate : class {
    func didPickColor(color : UIColor)
}

We have defined one protocol here. Which has one function. Any Class which promises to confirm to this protocol must implement this function.(Promises are kept in programming world).

Declare one variable in ColorPickerViewController

weak var delegate : ColorPickerDelegate?

The is a weak variable. Delegates are generally kept as weak to avoid retain cycle. Because most of the time you will find that the Class which promises to become the Delegate of some class, already holds the reference to class for which its promising to be Delegate. So to avoid retain cycle we declare delegate variable to be weak. Don’t panic if you don’t get right now. Its a separate topic. So now we have delegate variable which can hold value of only that type which conforms to ColorPickerDelegate.

Next step would be to notify the delegate that some button is tapped, and which color is picked. Modify the button actions to do this as following

    @IBAction func redButtonClicked(_ sender: Any) {
        self.delegate?.didPickColor(color: UIColor.red)
    }
    @IBAction func blueButtonClicked(_ sender: Any) {
        self.delegate?.didPickColor(color: UIColor.blue)
    }
    
    @IBAction func yellowButtonClicked(_ sender: Any) {
                self.delegate?.didPickColor(color: UIColor.yellow)
    }

The above functions tell the delegate that some color has been picked. Now for whoever is delegate of this class didPickColor(color:) will be called. We are done with the picker controller part. Its responsibility was to show the color options and notify its delegate when one of the colors is picked. Now its upto the delegate to do whatever it wants to do with the color. It can apply the picked color to some text, label etc. in our case we will change the background color.

After doing above changes your ColorPickerViewController file should look like this

import UIKit

protocol ColorPickerDelegate : class {
    func didPickColor(color : UIColor)
}

class ColorPickerViewController: UIViewController {
    weak var delegate : ColorPickerDelegate?
    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    
    @IBAction func redButtonClicked(_ sender: Any) {
        self.delegate?.didPickColor(color: UIColor.red)
    }
    @IBAction func blueButtonClicked(_ sender: Any) {
        self.delegate?.didPickColor(color: UIColor.blue)
    }
    
    @IBAction func yellowButtonClicked(_ sender: Any) {
                self.delegate?.didPickColor(color: UIColor.yellow)
    }



}

Now go to ViewController class and make it conform to ColorPickerDelegate like this

class ViewController: UIViewController,ColorPickerDelegate {

We have just added ColorPickerDelegate after comma. Now we have promised that ViewController class conforms to ColorPickerDelegate. But does it? NO!
To conform to ColorPickerDelegate we should implement its function didPickColor(color : UIColor). So lets keep our promise.

    func didPickColor(color : UIColor) {
        self.dismiss(animated: true, completion: nil)
        self.view.backgroundColor = color
    }

We have now implemented didPickColor(color : UIColor) function. We are changing background color of the view to the color picked from picker.
We are almost done. Just one piece is missing. We have not yet assigned the delegate variable of the ColorPickerViewController to ViewController class instance. Go to prepare(for segue: UIStoryboardSegue, sender: Any?) and grab the instance of ColorPickerViewController and assign its delegate to current view controller i.e. self

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        let pickerController = segue.destination as! ColorPickerViewController
        pickerController.delegate = self
    }

We are done now. Just a quick recap of steps

  1. We declared the protocol that contains the function to be implemented by the delegate instance
  2. We declared one variable in ColorPickerViewController to hold the delgate instance
  3. We called the function on delegate when any of the buttons was clicked
  4. We promised that ViewController class would conform to ColorPickerDelegate
  5. We made ViewController class to actually conform to ColorPickerDelegate by implementing the function required by ColorPickerDelegate.
  6. We assigned the delegate variable of ColorPickerViewController to ViewController instance.

Thats it! Now run the app. If you have done everything right you will be able to pick the color, that will be applied to background of ViewController.
The code in ViewControllershould look like following after you are done.

import UIKit

class ViewController: UIViewController,ColorPickerDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    }
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        let pickerController = segue.destination as! ColorPickerViewController
        pickerController.delegate = self
    }
    
    func didPickColor(color : UIColor) {
        self.dismiss(animated: true, completion: nil)
        self.view.backgroundColor = color
    }

}

You can download the completed code from here.
Thats it folks. I hope you enjoyed the article. HAPPY LEARNING!

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s