Remember how awesome the delegate pattern was in Objective-C? Enabling classes to be super reusable by delegating out the bits that you might want control over. There’s a reason that it’s so ubiquitous across Cocoa Touch, it’s just so damn good!

But there’s a new pattern that seems to be quietly replacing the delegate pattern. I’m not sure if it has an official name, but I call it:

The Closure Callback Pattern

You probably know which one I mean, but in case you need a refresher, here’s the delegate way of doing things:

protocol ImageDownloaderDelegate: class {

func imageDownloader(_ downloader: ImageDownloader, didDownloadImage image: UIImage)

} class ImageDownloader { weak var delegate: ImageDownloaderDelegate? func downloadImage(url: URL) {

// download the image asynchronously then...

delegate?.imageDownloader(self, didDownloadImage: theImage)

}

}

and here’s the closure callback way:

class ImageDownloader { var didDownload: ((UIImage?) -> Void)? func downloadImage(url: URL) {

// download the image asynchronously then...

didDownload?(theImage)

}

}

Like everyone else, when Swift came out I dabbled with the closure callback pattern, using it where I might have previously used a delegate in a quest for ever increasing ‘Swifty-ness’. I was left unsatisfied though. Sometimes it felt like an improvement, and sometimes it didn’t. Is the delegate or the closure callback the superior pattern? I guess, well, it depends.

Depends on what, though??? Let’s compare these two approaches and see if we might learn something.

Breaking the retain cycle

Whenever two objects reference each other, one of them has to hold a weak reference to the other or we get a retain cycle. The handling of this couldn’t be more different between these two patterns.

How the delegate pattern does it:

class ImageDownloader {

weak var delegate: ImageDownloaderDelegate?

//...

} class ImageViewer: ImageDownloaderDelegate { let downloader: ImageDownloader() init(url: URL) {

downloader.delegate = self

downloader.downloadImage(url: url)

} func imageDownloader(_ downloader: ImageDownloader, didDownloadImage image: UIImage) {

// view the downloaded image...

}

}

The ImageDownloader is responsible for breaking the retain cycle by holding a weak reference to it's delegate

is responsible for breaking the retain cycle by holding a reference to it's delegate The weak / strong relationship only has to be defined in a single place

Convention is for delegates to be held weakly, and a linter like SwiftLint will warn you if you forget to do this

How the closure callbacks do it:

class ImageDownloader {

var didDownload: ((UIImage?) -> Void)?

//...

} class ImageViewer { let downloader: ImageDownloader init(url: URL) {

downloader = ImageDownloader()

downloader.downloadImage(url: url)

downloader.didDownload = { [weak self] image in

// view the image

}

}

}

The ImageViewer is responsible for making sure that it's references itself weakly in the callback

is responsible for making sure that it's references itself weakly in the callback The weak / strong relationship must be correctly implemented in every callback

It’s easy to make a mistake a cause a memory leak

It may be the old way of doing things, but the delegate pattern is a clear winner on this front. The weak relationship is only defined once, and it's much harder to make a mistake.

There’s plenty more to consider though, let’s see if the closure callbacks can redeem themselves…

One to many relationships

What if one class needs use multiple ImageDownloader s, how might our two patterns fair then?

First up, the delegate pattern:

class ProfilePage: ImageDownloaderDelegate { let profilePhotoDownloader = ImageDownloader()

let headerPhotoDownloader = ImageDownloader() init(profilePhotoUrl: URL, headerPhotoUrl: URL) { profilePhotoDownloader.delegate = self

profilePhotoDownloader.downloadImage(url: profilePhotoUrl) headerPhotoDownloader.delegate = self

headerPhotoDownloader.downloadImage(url: headerPhotoUrl)

} func imageDownloader(_ downloader: ImageDownloader, didDownloadImage image: UIImage) { if downloader === profilePhotoDownloader {

// show the profile photo...

} else if downloader === headerPhotoDownloader {

// show the profile photo...

}

}

}

We have to check which instance of the ImageDownloader is calling us in each callback. If you have a whole bunch of delegate methods this is going to get really tedious. Plus you're likely to make a mistake.

I’m sure we’ve all worked on an app before where the same object was the delegate for multiple UITableView s. Not cool.

Let’s see if the closure callback pattern can save us:

class ProfilePage { let profilePhotoDownloader = ImageDownloader()

let headerPhotoDownloader = ImageDownloader() init(profilePhotoUrl: URL, headerPhotoUrl: URL) { profilePhotoDownloader.didDownload = { [weak self] image in

// show the profile image

}

profilePhotoDownloader.downloadImage(url: profilePhotoUrl) headerPhotoDownloader.didDownload = { [weak self] image in

// show the header image

}

headerPhotoDownloader.downloadImage(url: headerPhotoUrl)

}

}

It’s a clear win. The callbacks for the two instances are completely separate, so there’s no chance of us getting them mixed up.

The closure callbacks win this one. It’s 1–1. Let’s see what’s next:

Datasources

Ok, these aren’t strictly delegate patterns, but I’ve seen the closure callbacks used to supply information to an object too, so I’m including them.

Lets look at a protocol based datasource pattern:

protocol SerialImageUploaderDataSource: class {

var numberOfImagesToUpload: Int { get }

func image(atIndex index: Int) -> UIImage

func caption(atIndex index: Int) -> String

} class SerialImageUploader { weak var dataSource: SerialImageUploaderDataSource? init(dataSource: SerialImageUploaderDataSource) {

self.dataSource = dataSource

} func startUpload() { guard let dataSource = dataSource else { return } for index in 0..<dataSource.numberOfImagesToUpload {

let image = dataSource.image(atIndex: index)

let caption = dataSource.caption(atIndex: index)

upload(image: image, caption: caption)

}

} func upload(image: UIImage, caption: String) {

// Upload the image...

}

}

The protocol methods are all required, so if the dataSource exists, then we know that it has implemented all of the methods that we require

exists, then we know that it has implemented all of the methods that we require We passed the data source in to the init method, so we're making it clear to the user of this class that a data source is required

method, so we're making it clear to the user of this class that a data source is required If we add required methods to the datasource protocol later, we’ll get a compiler error until we implement them

Now a Datasource implemented with closures:

class SerialImageUploader { var numberOfImagesToDownload: (() -> Int)?

var imageAtIndex: ((Int) -> UIImage)?

var captionAtIndex: ((Int) -> String)? func startUpload() { guard

let numberOfImagesToDownload = numberOfImagesToDownload,

let imageAtIndex = imageAtIndex,

let captionAtIndex = captionAtIndex

else {

return

} for index in 0..<numberOfImagesToDownload() {

let image = imageAtIndex(index)

let caption = captionAtIndex(index)

upload(image: image, caption: caption)

}

} func upload(image: UIImage, caption: String) {

// Upload the image...

}

}

This one’s a bit of a train-wreck. We rely on all three closures being non-nil, so I had to guard against all of them. I could have passed them all in the init and made them non-optional, but I think that would be a bit ridiculous.

If you just require a single closure then you can supply it in the init as a non-optional, otherwise using a protocol is clearly the better approach.

Scalability

So, we’ve just got one method now, but what if we have 10 in the future? Our protocol now looks like this:

Delegate:

protocol ImageDownloaderDelegate: class {

func imageDownloader(_ downloader: ImageDownloader, didDownloadImage image: UIImage)

func imageDownloaderDidFail(_ downloader: ImageDownloader)

func imageDownloaderDidPause(_ downloader: ImageDownloader)

func imageDownloaderDidResume(_ downloader: ImageDownloader)

} extension ViewController: ImageDownloaderDelegate { func imageDownloader(_ downloader: ImageDownloader, didDownloadImage image: UIImage) {

} func imageDownloaderDidFail(_ downloader: ImageDownloader) {

} func imageDownloaderDidPause(_ downloader: ImageDownloader) {

} func imageDownloaderDidResume(_ downloader: ImageDownloader) {

}

}

We’ve got all of the delegate methods neatly wrapped up in an extension

It’s really clear how all of this works

Closure callbacks:

class ImageDownloader {

var didDownload: ((UIImage?) -> Void)?

var didFail: (() -> ())?

var didPause: (() -> ())?

var didResume: (() -> ())?

} class ViewController: UIViewController { let downloader = ImageDownloader() override func viewDidLoad() {

super.viewDidLoad() downloader.didDownload = {

//...

} downloader.didFail = {

//...

} downloader.didPause = {

//...

} downloader.didResume = {

//...

}

}

}

I don’t like this at all. It’s not clear where we should even put the setup code for all the callbacks. Maybe we could make a method called setupDelegateCallbacks() ? It's all a bit messy for my liking.

Another win for the delegate pattern.

And on to our last test!

Enforcing the contract

Any type that works with another type should expect the other type to adhere to a contract. That way, if that contract isn’t fulfilled then the compiler can let us know at compile time, and we can avoid nasty surprises at runtime.

Lets look at how type-safe these two approaches are.

Delegates:

Add a new method, get a compiler error, sweet!

Closures callbacks:

Add a new callback and you’ll be blissfully unaware that you haven’t implemented it. Which might have bad consequences. Hope it wasn’t important!

So which is better?

So which is best? As we already knew, it depends! But hopefully we have a better idea on what now, so lets try to lay down some guidelines:

Scenario 1:

You have a single callback

The callback closures pattern is the best here. Pass it in the initialiser, and you can even make it non-optional:

class ImageDownloader { var onDownload: (UIImage?) -> Void init(onDownload: @escaping (UIImage?) -> Void) {

self.onDownload = onDownload

}

}

Scenario 2:

Your callbacks are more like notifications

If your callbacks are more like ‘notifications’ or ‘triggers’ that fire when other things happen, then the closure callbacks can be a less invasive option. Keep them optional, and you can just implement the ones that you are interested in.

If your delegator is saying Hey I just did this thing btw, letting you know, rather than I really need you to do something now! an optional closure can let you subscribe to that callback if you need it, or not if you don’t.

Scenario 3:

You need to become the delegate to multiple instances

The closure callback is the better pattern here. Be careful that this is really what you require though. You can always have an object that is a dedicated delegate or datasource, and have many instances of those.

Scenario 3:

Your delegate is actually a datasource

Use a protocol, it enforces a stronger contract between the two types, and the compiler can help you find bugs.

Scenario 4:

Your have many callbacks, and they might change in the future

Use a protocol. If you forget to implement new methods in the future, the compiler will tell you rather than your users.

Anything else, or you’re not sure:

If in doubt, use a protocol. Defining a protocol guarantees that conforming types will have implemented the specified methods. If the protocol requirements change the future, the compiler will require you to update your types. It also simplifies the weak / strong relationship, allowing you to define it in a single place.

Conclusion

The callback closures pattern seems to be creeping in everywhere. It can be a great way to reduce complexity, handle one to many relationships, and make code more readable. I still think that a protocol is more appropriate for the majority of cases, though.

Choose the write tool for the right job, and if you only remember one thing from this post — never become the delegate to two UITableView s!

Further reading

This post by Oleg Dreyman presents a nice solution for avoiding the pitfalls of the weak / strong dance using closure callbacks

This post by John Sundell has some nice examples of closure callbacks vs. delegates.

Thoughts / comments / complaints / just wanna chat? Tweet me.