Swift · · 10 min read

Understanding Delegates and Delegation in Swift

Understanding Delegates and Delegation in Swift

I’m going to talk about “delegates” and “delegation.” I’ll lead you through a simple example of implementing the delegation design pattern in Swift 4, with full source code. My intent here is to show you how delegation works without getting bogged down in some crazy complex example. To help you become the best of the best, I’m going to introduce you to one of the greatest design tools to aid in object-oriented software development, UML. I’ll show you a UML diagram that I drew up to design and document the implementation of the delegation design pattern used in the sample app we’ll build together.

I’ll show you how to build a user interface (UI) helper, a class that downloads a file at a specified URL. Most importantly, I’ll show you how, through delegation, a UIViewController subclass can be notified by the helper that an image file has finished downloading, and then the view controller can display the image on screen. For the sake of simplicity and clarity, we’ll pretend that Swift has minimal support for downloading a file from a URL. We’ll manually wire up the notification that the file has finished downloading using the delegation design pattern. Here’s the app we’ll build:

Swift Delegate Demo

Recommended reading

In order to help you in understanding how I build my sample delegation code herein, you should read the following articles:

Delegation

Let’s start with a layman’s definition of some terms.

The dictionary definition of “delegate” (Cambridge) is “to give a particular job, duty, right, etc. to someone else so that they do it for you.” The dictionary definition of “delegation” (Merriam-Webster) is “the act of empowering to act for another.”

In a nutshell, “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.”

You’ve all probably used delegation. It would be hard not to in iOS.

Think of a UIViewController that you’ve subclassed (call it ViewController) to manage a UICollectionView. ViewController includes a UICollectionView instance (call it collectionView). You set the collectionView instance’s delegate property to self and self is ViewController.

ViewController adopts the UICollectionViewDelegate protocol. The delegate is ViewController. To conform to the UICollectionViewDelegate protocol, ViewController implements methods like collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath).

By implementing the didSelectItemAt method, ViewController gets informed of taps on UICollectionViewCell objects. When a cell is tapped, the ViewController implementation of didSelectItemAt is called. You get to define the body of the didSelectItemAt method so that, for example, when a cell is tapped, you can visually highlight the cell and perform some application-specific logic. collectionView has delegated the responsibility of handling taps on UICollectionViewCell objects to ViewController.

Here’s an example of delegation and UICollectionViewDelegate (click link).

According to Apple:

Delegation is a simple and powerful pattern in which one object in a program acts on behalf of, or in coordination with, another object. The delegating object keeps a reference to the other object–the delegate–and at the appropriate time sends a message to it. The message informs the delegate of an event that the delegating object is about to handle or has just handled. The delegate may respond to the message by updating the appearance or state of itself or other objects in the application, and in some cases it can return a value that affects how an impending event is handled. The main value of delegation is that it allows you to easily customize the behavior of several objects in one central object. …

and also:

A delegate is an object that acts on behalf of, or in coordination with, another object when that object encounters an event in a program. The delegating object is often a responder object–that is, an object inheriting from NSResponder in AppKit or UIResponder in UIKit–that is responding to a user event. The delegate is an object that is delegated control of the user interface for that event, or is at least asked to interpret the event in an application-specific manner. …

The programming mechanism of delegation gives objects a chance to coordinate their appearance and state with changes occurring elsewhere in a program, changes usually brought about by user actions. More importantly, delegation makes it possible for one object to alter the behavior of another object without the need to inherit from it. The delegate is almost always one of your custom objects, and by definition it incorporates application-specific logic that the generic and delegating object cannot possibly know itself. …

Requirement: a protocol

In order for me to write sample code implementing the delegation design pattern, I’ll need a “protocol.” Refer to my article on protocols and remember that, as Apple puts it (my emphasis added):

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. This design pattern is implemented by defining a protocol that encapsulates the delegated responsibilities, such that a conforming type (known as a delegate) is guaranteed to provide the functionality that has been delegated. Delegation can be used to respond to a particular action, or to retrieve data from an external source without needing to know the underlying type of that source.

Apple notes that:

A protocol defines a blueprint of methods, properties, and other requirements that suit a particular task or piece of functionality. The protocol can then be adopted by a class, structure, or enumeration to provide an actual implementation of those requirements. Any type that satisfies the requirements of a protocol is said to conform to that protocol. …

Explaining the code

Note: Keep referring to my prose and quotations above to remind yourself about the very important terminology — words, definitions — I’m using in the following discussion.

Piece: the delegating object

I’m going to build a class that implements an image downloader, the LogoDownloader class, something that, given a URL, can download a file asynchronously and provide notice upon download completion. This will be the “delegating object,” as defined above, that “keeps a reference to the other object–the delegate–and at the appropriate time sends a message to it.”

Piece: the delegate protocol

A protocol needs to be defined. Remember that the delegation “design pattern is implemented by defining a protocol that encapsulates the delegated responsibilities, such that a conforming type (known as a delegate) is guaranteed to provide the functionality that has been delegated” (my emphasis added). This will be the LogoDownloaderDelegate protocol.

Piece: the delegate

I’m going to subclass a UIViewController and make it adopt — conform to — the delegate protocol LogoDownloaderDelegate. Remember that the delegation “design pattern is implemented by defining a protocol that encapsulates the delegated responsibilities, such that a conforming type (known as a delegate) is guaranteed to provide the functionality that has been delegated” (my emphasis added). I’ll call this class ViewController (my, how original).

UML: how the pieces fit together

I drew a UML diagram before I got very far into the code. UML is the best tool I’ve found for designing object-oriented software. Take a look at my diagram, refer to the legend, and read these references on UML here, here, here, and here. Here’s the diagram:

UML Diagram

Swift Delegate Diagram

Code demo

Here’s what the app I’m building does:

Swift Delegate Demo

Writing the code

Piece: the delegating object

Let’s look at the code for the delegating object, LogoDownloader, which can download an (image) file. I keep an optional reference to the delegate object (of type LogoDownloaderDelegate) to allow a LogoDownloader class instance to operate without a delegate. If the delegate member variable is nil, LogoDownloader still downloads a file and still calls its own didDownloadImage() method.

var delegate:LogoDownloaderDelegate?

Here’s LogoDownloader.swift — and note the optional (?) “delegate” property and note where the delegate variable is sending the message delegate?.didFinishDownloading(self):

import UIKit

//
// This is the DELEGATING CLASS. An instance of this
// class is a DELEGATING OBJECT.
//
class LogoDownloader {
    
    var logoURL:String
    
    var image:UIImage?
    
    // weak var delegate:LogoDownloaderDelegate?
    // SEE NOTE BELOW
    var delegate: LogoDownloaderDelegate?
    
    init(logoURL:String) {
        self.logoURL = logoURL
    }
    
    func downloadLogo() {
        // Start the image download task asynchronously by submitting
        // it to the default background queue; this task is submitted
        // and DispatchQueue.global returns immediately.
        DispatchQueue.global(qos: DispatchQoS.QoSClass.default).async {
    
            // I'm PURPOSEFULLY downloading the image using a synchronous call
            // (NSData), but I'm doing so in the BACKGROUND.
            let imageURL = URL(string: self.logoURL)
            let imageData = NSData(contentsOf: imageURL!)
            self.image = UIImage(data: imageData! as Data)
            print("image downloaded")
    
            // Once the image finishes downloading, I jump onto the MAIN
            // THREAD TO UPDATE THE UI.
            DispatchQueue.main.async {
                // Tell the delegate that the image
                // has downloaded so the delegate can
                // display the image.
                self.didDownloadImage()
            }
    
        } // end DispatchQueue.global
    }
    
    // Since this class has a reference to the delegate,
    // "at the appropriate time [it] sends a message to" the delegate.
    // Finishing the logo download is definitely
    // the appropriate time.
    func didDownloadImage() {
        delegate?.didFinishDownloading(self)
    }
    
} // end class LogoDownloader

NOTE: Technically, I should’ve marked the declaration of the delegate as:

weak var delegate:LogoDownloaderDelegate?

to avoid retain cycles. Notice that LogoDownloaderDelegate is just a protocol. I would’ve had to introduce an intermediate class, and understanding delegates is difficult enough. I didn’t want to cause people more confusion than they already have when trying to understand delegates.

If I did mark the delegate declaration as weak, I would’ve received the error message, “‘weak’ may only be applied to class and class-bound protocol types, not ‘LogoDownloaderDelegate’.”

You can see a delegate declared as weak in Objective-C in my tutorial on “Tutorial: delegates and delegation in Objective-C”. Objective-C is more tolerant.

Piece: the delegate protocol

LogoDownloaderDelegate is the delegate protocol. You can conceptualize a protocol as a contract or promise that you can apply to a class, structure, or enumeration. Below, I will enter my ViewController class into a contract with the LogoDownloaderDelegate protocol, and the ViewController class promises to fulfill the contract by implementing the methods or member variables that LogoDownloaderDelegate requires be materialized or fulfilled, i.e., implemented.

Here’s LogoDownloaderDelegate.swift:

import Foundation

import UIKit

//
// This is the DELEGATE PROTOCOL
//
protocol LogoDownloaderDelegate {
    // Classes that adopt this protocol MUST define
    // this method -- and hopefully do something in
    // that definition.
    func didFinishDownloading(_ sender:LogoDownloader)
}
Exercise:

Why do you think I’ve specified a reference to the LogoDownloader object in the didFinishDownloading(_) method declaration?

Piece: the delegate

My ViewController class adopts — conforms to — the delegate protocol LogoDownloaderDelegate. Notice that:

1) it has an optional (?) instance of LogoDownloader (var logoDownloader:LogoDownloader?);

2) I’m using optional chaining, “a process for querying and calling properties, methods, and subscripts on an optional that might currently be nil. If the optional contains a value, the property, method, or subscript call succeeds; if the optional is nil, the property, method, or subscript call returns nil. Multiple queries can be chained together, and the entire chain fails gracefully if any link in the chain is nil;”

3) it sets the LogoDownloader instance’s member variable delegate to self; and,

4) it provides customization of LogoDownloader by allowing me to do whatever I want when those protocol methods are called, like animating the display of the image when it has finished downloading.

Here’s ViewController.swift:

import UIKit

// NOTE: I set auto layout constraints for the view controller
// in the storyboard corresponding to this ViewController
// for "View as: iPhone SE."

//
// This is the DELEGATE
//
class ViewController: UIViewController, LogoDownloaderDelegate {
    
    @IBOutlet weak var loadingLabel: UILabel!
    @IBOutlet weak var loginView: UIView!
    @IBOutlet weak var imageView: UIImageView!
    
    var logoDownloader:LogoDownloader?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        
        // Initially, the image view is hidden so we can fade it in with animation.
        imageView.alpha = 0.0
        
        // Initially, the login area, with username and password, are hidden
        // until the logo image downloads, and then we fade it in.
        loginView.alpha = 0.0
        
        // NASA images used pursuant to https://www.nasa.gov/multimedia/guidelines/index.html
        let imageURL: String = "https://cdn.spacetelescope.org/archives/images/publicationjpg/heic1509a.jpg"
        
        // Construct a LogoDownloader to download the NASA file.
        logoDownloader = LogoDownloader(logoURL: imageURL)
        // Set a reference in the delegating object, LogoDownloader, to
        // this class, ViewController. ViewController is the delegate.
        // LogoDownloader tells ViewController that the image at the NASA
        // URL has downloaded by calling the delegate method
        // didFinishDownloading(_).
        logoDownloader?.delegate = self // try nil here
        // Start the logo image download and get informed when it
        // finished downloading when didFinishDownloading(_) is called.
        logoDownloader?.downloadLogo()
        
        // Since the delegating object, LogoDownloader, has an optional
        // reference to this class, ViewController, that reference can
        // be nil, and since we use optional chaining, ViewController
        // can run with or without the delegating object.
        if logoDownloader?.delegate == nil {
            loginView.alpha = 1.0
        }
    } // end func viewDidLoad()
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    
    // Defining this method makes ViewController conform/adopt
    // the LogoDownloaderDelegate protocol. This method is called
    // when the logo image finished downloading.
    func didFinishDownloading(_ sender: LogoDownloader) {
        imageView.image = logoDownloader?.image
        
        // Animate the appearance of this ViewController's
        // user interface.
        UIView.animate(withDuration: 2.0, delay: 0.5, options: UIViewAnimationOptions.curveEaseIn, animations:  {
            self.loadingLabel.alpha = 0.0
            self.imageView.alpha = 1.0
        }) { (completed:Bool) in
            if (completed) {
                UIView.animate(withDuration: 2.0) {
                    self.loginView.alpha = 1.0
                }
            }
        }
    } // end func didFinishDownloading
    
} // end class ViewController

Wrapping up

I want you to thoroughly read and digest my initial discussion about delegation, follow some of the links, and read some of my suggested articles. Notice I’m not explaining every little detail. When looking at the code, I want you to think about what’s going on. Look something up in one of my references or find others if you don’t understand a concept. Write your own delegation code.

Above all, keep practicing and getting experience — and please enjoy your work!

Read next