Implement Delegate pattern in Swift
While teaching iOS development in Swift at iOS Akademija (here in Belgrade, Serbia), one of the most challenging topics for students to grasp is Delegate pattern.
I’m not sure why that is; most likely due to loose coupling and very asynchronous nature of the relationships between the relevant parties. Which are the very strengths of the pattern but those multiple moving parts are certainly adding to complexity. It takes some time and experience to recognize the need and the place for the pattern and internalize its implementation.
Teaching approach I like to use is to find a corresponding real-world problem and then map it into the pattern. So…
- Imagine a person lost its house Key thus needs a new one made.
- Person does not care how keys are made. How long it takes (well, within common sense). What tools are needed etc.
- It only knows how the Key looks like and needs someone to do it.
- Person finds the Keymaker that makes such specific Key type.
- Goes to the shop and
- registers its contact information so Keymaker can make contact when job is done
- agrees with Keymaker how it will know when it’s done, meaning how it will receive the Key
- then waits for the call / delivery of the Key
- Keymaker will make the Key in due time and when it’s ready, it
- looks for the customer’s contact info
- then calls customer and delivers the Key
This is perfect fit for the Delegate pattern. So let’s implement it in Swift.
First, as in all good apps, we need to create proper data model for the Key
== a description of work that needs to be done.
struct Key {
var shape: String
}
Next, we need to model the Person
, end user == the object that needs some work done.
final class Person {
var name: String = "Me"
var key: Key?
}
Lastly, let’s model the service which performs some function. Here Keymaker
which makes the specific Key
.
final class Keymaker {
weak var customer: Keymaking?
}
Property customer
models registration point for end users that need this specific work — key of type Key
— done. In most iOS APIs, you will see the name delegate
being used here; which is the common name used when implementing Delegate pattern. But that’s just developers using the same property name as the pattern itself. The actual name of the property is irrelevant and you can use whatever you want.
Notice two things here:
customer
is a singular item which implies the Keymaker can only work with one customer at the time. Thus it’s your job as developer to make sure that if one object assign itself ascustomer
, no other object overrides or nullifies that assignment. Otherwise originalPerson
will never receive itsKey
.Keymaker
does not care what is the real, actual type of the entity/object that needs theKey
made. It’s notPerson
,Company
nor anything concrete. It’s actually a special abstract type, declared by theKeymaker
which describes what any customer needs to implement in order to work with the Service.
In Swift, that’s done through protocols, which represent output interface declared by the Service to describe how it will deliver the result of its work.
protocol Keymaking: class {
func keymaker(_ keymaker: Keymaker, didProduceKey key: Key)
}
Hence by saying that customer
is of type Keymaking
, Service actually says:
- I will work with anyone, as long as
- that someone agrees with the way I deliver my results
Person
needs to indicate said agreement by adopting and implementing the given Keymaking
protocol.
extension Person: Keymaking {
func keymaker(_ keymaker: Keymaker, didProduceKey key: Key) {
self.key = key
}
}
Note that Keymaker does not care what its customer will do with the key. So it’s Person
’s free will what will be inside this method above.
That’s why Swift protocols are perfect tool for the implementation of the Delegate pattern. They declare the intent and interface but don’t care about the actual implementation.
When Keymaker
has produced the Key
, it looks up customer’s contact info and makes the call / delivery using the method in its protocol:
fileprivate extension Keymaker {
func didProduceKey(_ key: Key) {
customer?.keymaker(self, didProduceKey: key)
}
}
Note: this part is usually marked as (file)private for the Keymaker
. No object outside the Keymaker
should know nor care how the Key
is produced.
Because you will usually have Key
, Person
and Keymaker
all written in separate corresponding .swift
files, then fileprivate
helps Keymaker keep its trade secrets. Keymaker can change its internal process without fear that some nosy customer is trying to directly access parts of the Key making process. :)
I made example playground for all this code, which you can pickup from the repository at GitHub.com/radianttap.
I hope this was useful in understanding such crucial software design pattern. It’s very pervasive in iOS SDK thus any app developer needs to master it.
You will find it with UITextField
which allows any other object conforming to UITextFieldDelegate
protocol to control parts of its behavior and receive information when editing starts, stops etc.
UITableView
uses it to offload input data to anyone implementing UITableViewDataSource
. Through UITableViewDelegate
it allows any other object to control parts of its look and behavior plus stay informed when something happens (i.e. row is selected).
Most importantly, Delegate pattern — along with Dependency Injection pattern — enables easy and straight-forward Composition of otherwise unrelated objects, views and controls. Mastering that technique places you well on the path to clean and easily maintainable code.