Generic Delegates in Swift

June 16, 2016

Recently, I stumbled over a problem as I wanted to integrate a delegate with generic function definitions. I created a protocol with an associatedType to make it generic. The protocol (simplified) looked basically like this:

protocol StackDelegate  {
    associatedtype T
    func didPushElement(element: T, stack: Stack<T>)
}

I simply wanted to add the delegate property to my receiving object,

struct Stack<T> {
    var delegate : StackDelegate?
    ...
}

but then I found myself fighting with the following error:

Protocol 'StackDelegate' can only be used as a generic
constraint because it has Self or associated type
requirements

It turns out that when using a generic protocol in this way, the compiler can’t infer a concrete type during compilation. I found a great article that explains this problem in depth: Generic Protocols & Their Shortcomings.

One way to solve this issue is to use a small helper struct called thunk, that acts as bridge between the delegate and the receiving object:

struct StackDelegateThunk<T> : StackDelegate {
   private let _didPushElement : (T, Stack<T>) -> Void

   init<P:StackDelegate where P.T == T>(_ delegate: P) {
       _didPushElement = delegate.didPushElement
   }

   func didPushElement(element: T, stack: Stack<T>) {
       _didPushElement(element, stack)
   }
}

It forwards the calls from the receiving object to the delegate. „Using this, we can effectively erase our abstract generic protocol types in favor of another concrete, more fully-fledged type. This is often referred to as type erasure.” 1

Having the struct in place, the delegate declaration can replaced with the following lines:

struct Stack<T> {
    private(set) var delegate : StackDelegateThunk<T>?

    mutating func setDelegate<P:StackDelegate where P.T == T>(delegate: P) {
        self.delegate = StackDelegateThunk<T>(delegate)
    }
    ...
}

That’s basically it. A playground with a full example can be found here.

[1] Generic Protocols & Their Shortcomings