Swift, delegates and generics

Hi,


I'm trying to achieve something like the following:


Concrete class:

class MyClass<Object: AnyObject> : NSObject, MyProtocol {
     // [...]
     func doSomethingForObject(obj: Object) -> Int {
        // [...]
     }
     // [...]
     weak var delegate: MyClassDelegate?
     private var updates: [Update<Object>] = []
}


This class conforms to the protocol MyProtocol:

protocol MyProtocol: class {
     typealias Object
     // [...]
     func doSomethingForObject(obj: Object) -> Int
     // [...]
}


Here is the delegate protocol:

protocol MyClassDelegate: class {
     typealias Object
     typealias AClass: MyProtocol
     func myClass(myClass: AClass, didUpdate updates: [Update<Object>]?)
}


I've tried different methods to approach this result but I eventually always get one of the following error:

- Protocol MyProtocol / MyClassDelegate can only be used as a generic constraint because it has Self or associated type requirements.

- Cannot invoke 'myClass' with an argument list of type '(MyClass<Delegate>, didUpdate: [Update]?)'

- Circular type reference error


Is it a current limitation of Swift or is there some other way to achieve a similar result ?


Thanks.

Accepted Reply

A closure for the update handler alone would do it. However, my delegate contains many other methods.


I went back to a cleaner (and working) solution. Instead of having a delegate that I reuse for all classes conforming to the protocol (MyProtocol in the example above), I create a delegate for each class. It now looks like this:


protocol MyClassDelegate: class {
     typealias Object
     func myClass(myClass: MyClass<Self>, didUpdate updates: [Update<Object>]?)
}

class MyClass<Delegate: MyClassDelegate> : NSObject, MyProtocol {
     typealias Object = Delegate.Object
     // [...]
     func doSomethingForObject(obj: Object) -> Int {
        // [...]
     }
     // [...]
     weak var delegate: Delegate?
     private var updates: [Update<Object>] = []
}


protocol MyProtocol: class {
     typealias Object
     // [...]
     func doSomethingForObject(obj: Object) -> Int
     // [...]
}

Replies

My first thought was this. Does it come with problems?

MyClass<Object: AnyObject, Delegate: MyClassDelegate>
weak var delegate: Delegate?

Unfortunately yes, in this case (using <Object: AnyObject, Delegate: MyClassDelegate>) I get the error "Type 'MyClass<Object, Delegate>' does not conform to protocol MyProtocol."


What I tried before was quite close:

MyClass<Delegate: MyClassDelegate> : NSObject, MyProtocol {
     typealias Object = Delegate.Object
     // [...]
}


But in this case, I get the error "Cannot invoke 'myClass' with an argument list of type '(MyClass<Delegate>, didUpdate: [Update]?)'" for this line:

delegate.myClass(self, didUpdate: nil)

Have you considered using a closure for the update handler instead of using a delegate? Something like the following:


class MyClass<Object: AnyObject> : NSObject, MyProtocol {
  //...
  func doSomethingForObject(obj: Object) -> Int {
  //...
  }
  var updateHandler : (MyClass, Object)->()
  //...
}

A closure for the update handler alone would do it. However, my delegate contains many other methods.


I went back to a cleaner (and working) solution. Instead of having a delegate that I reuse for all classes conforming to the protocol (MyProtocol in the example above), I create a delegate for each class. It now looks like this:


protocol MyClassDelegate: class {
     typealias Object
     func myClass(myClass: MyClass<Self>, didUpdate updates: [Update<Object>]?)
}

class MyClass<Delegate: MyClassDelegate> : NSObject, MyProtocol {
     typealias Object = Delegate.Object
     // [...]
     func doSomethingForObject(obj: Object) -> Int {
        // [...]
     }
     // [...]
     weak var delegate: Delegate?
     private var updates: [Update<Object>] = []
}


protocol MyProtocol: class {
     typealias Object
     // [...]
     func doSomethingForObject(obj: Object) -> Int
     // [...]
}

How does that compile for you? Line 13 shouldn't compile as MyClassDelegate has an associated type constraint, so you can't use it in a type declaration.

Thanks, I've fixed the example.

Must be Delegate on line 13, not MyClassDelegate.