The original article – Understanding Delegates and Delegation in Swift 4 was published on appcoda.com.
[Download Xcode 9 project with full Swift 4 source from GitHub.]
Introduction
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. Download the Xcode 9 project with full Swift 4 source from GitHub to follow along.
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:
Recommended reading
In order to help you in understanding how I build my sample delegation code herein, you should read my articles on “Using Swift extensions to manage complexity, improve readability, and increase extensibility (UICollectionView, protocols, and delegates)” and “Understanding Swift 4 protocols and using them in your apps.”
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 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:
Code demo
Here’s what the app I’m building does:
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.
1 |
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)
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
import Foundation 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() -> Void { // 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:
1 |
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
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
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) } |
Homework: 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
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
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!
Hi. Just wondering about the declaration of the delegate:
var delegate:LogoDownloaderDelegate?
Shouldn’t it be marked as weak, to avoid retain cycles?
Adriano:
Yes, you’re correct, I should’ve marked the declaration of the delegate as:
weak var delegate:LogoDownloaderDelegate?
but 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’.”I’ll add a note to the article. 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.Thanks.
Andrew