Not at a computer right now, but in your last example, couldn’t you make the DummyDelegate protocol generic too, and then when declaring the weak var delegate then bind that to T?
With generic protocols currently absent from the language, there’s no good option for generic delegates right now. You can declare closure properties instead of a delegate, which uses the generic type in its signature, if you want to maintain the type safety.
The actual delegate I’m using has quite a few functions right now (and I’m adding even more). So moving from delegate to closure might not be very maintainable.
You can type-erase the delegate while hiding that from the callee if you so choose. From the client's perspective, the API appears very similar to a non-generic delegate pattern.
protocol DummyDelegate: AnyObject {
associatedtype T
func process(_: T)
}
class AnyDummyDelegate<T>: DummyDelegate {
private var _process: (T) -> Void
init<D: DummyDelegate>(_ delegate: D) where D.T == T {
_process = { [weak delegate] in delegate?.process($0) }
}
func process(_ t: T) {
_process(t)
}
}
class Dummy<T> {
private var delegate: AnyDummyDelegate<T>?
func setDelegate<D: DummyDelegate>(_ delegate: D) where D.T == T {
self.delegate = AnyDummyDelegate(delegate)
}
}
Note that the weak capturing of the delegate has moved from the class to the type-erased delegate.
It is also possible to create a type-erasing wrapper that does not use closures, as explained here or here or here or…
Type-erasing wrapper
protocol Foo {
associatedtype Bar
var x: Bar { get set }
}
private class _AnyFooBase<Bar>: Foo {
var x: Bar {
get { fatalError("Must override") }
set { fatalError("Must override") }
}
init() {
guard type(of: self) != _AnyFooBase.self else {
fatalError("AnyFooBase cannot be instantiated, use a subclass")
}
}
}
private final class _AnyFooBox<T: Foo>: _AnyFooBase<T.Bar> {
var wrapped: T
init(_ t: T) { wrapped = t }
override var x: T.Bar {
get { return wrapped.x }
set { wrapped.x = newValue }
}
}
final class AnyFoo<Bar>: Foo {
private let box: _AnyFooBase<Bar>
init<T: Foo>(_ foo: T) where T.Bar == Bar {
box = _AnyFooBox(foo)
}
var x: Bar {
get { return box.x }
set { box.x = newValue }
}
}