Swift delegate for general class

I have a class that needs to be called by a delegate when one of its properties changes. The following is a simplified class and protocol for the delegate:

protocol MyClassDelegate: class { func valueChanged(myClass: MyClass) } class MyClass { weak var delegate: MyClassDelegate? var currentValue: Int { didSet { if let actualDelegate = delegate { actualDelegate.valueChanged(self) } } } init(initialValue: Int) { currentValue = initialValue } } 

It all works great. But I want to make this class generic. So, I tried this:

 protocol MyClassDelegate: class { func valueChanged(genericClass: MyClass) } class MyClass<T> { weak var delegate: MyClassDelegate? var currentValue: T { didSet { if let actualDelegate = delegate { actualDelegate.valueChanged(self) } } } init(initialValue: T) { currentValue = initialValue } } 

This causes two compiler errors. First, a line declaring valueChanged in the protocol gives: Reference to generic type 'MyClass' requires arguments in <...> . Secondly, a call to valueChanged in the valueChanged observer throws: 'MyClassDelegate' does not have a member named 'valueChanged' .

I thought using typealias would solve the problem:

 protocol MyClassDelegate: class { typealias MyClassValueType func valueChanged(genericClass: MyClass<MyClassValueType>) } class MyClass<T> { weak var delegate: MyClassDelegate? var currentValue: T { didSet { if let actualDelegate = delegate { actualDelegate.valueChanged(self) } } } init(initialValue: T) { currentValue = initialValue } } 

I seem to be on the right track, but I still have two compiler errors. The second error remains at the top, as well as a new line declaring the delegate property of MyClass : Protocol 'MyClassDelegate' can only be used as a generic constraint because it has Self or associated type requirements .

Is there any way to do this?

+7
generics swift
Feb 19 '15 at 19:18
source share
3 answers

It is difficult to understand what is the best solution for your problem without additional information, but one possible solution is to change the protocol declaration to this:

 protocol MyClassDelegate: class { func valueChanged<T>(genericClass: MyClass<T>) } 

This eliminates the need for typealias in the protocol and should allow the error messages you received.

Part of the reason I'm not sure if this is the best solution for you is that I donโ€™t know how and where the valueChanged function is valueChanged , and therefore I donโ€™t know how practical it is to add a generic parameter to this function. If this solution does not work, send a comment.

+24
Feb 19 '15 at 19:30
source share

You can use template methods with erase type ...

 protocol HeavyDelegate : class { func heavy<P, R>(heavy: Heavy<P, R>, shouldReturn: P) -> R } class Heavy<P, R> { typealias Param = P typealias Return = R weak var delegate : HeavyDelegate? func inject(p : P) -> R? { if delegate != nil { return delegate?.heavy(self, shouldReturn: p) } return nil } func callMe(r : Return) { } } class Delegate : HeavyDelegate { typealias H = Heavy<(Int, String), String> func heavy<P, R>(heavy: Heavy<P, R>, shouldReturn: P) -> R { let h = heavy as! H // Compile gives warning but still works! h.callMe("Hello") print("Invoked") return "Hello" as! R } } let heavy = Heavy<(Int, String), String>() let delegate = Delegate() heavy.delegate = delegate heavy.inject((5, "alive")) 
+3
Aug 16 '15 at 13:30
source share

Protocols may have type requirements, but may not be general; and protocols with type requirements can be used as general restrictions, but they cannot be used to enter values. Because of this, you will not be able to reference your protocol type from your general class if you go this way.

If your delegation protocol is very simple (for example, one or two methods), you can accept closure instead of the protocol object:

 class MyClass<T> { var valueChanged: (MyClass<T>) -> Void } class Delegate { func valueChanged(obj: MyClass<Int>) { print("object changed") } } let d = Delegate() let x = MyClass<Int>() x.valueChanged = d.valueChanged 

You can extend the concept to a structure containing a bunch of closures:

 class MyClass<T> { var delegate: PseudoProtocol<T> } struct PseudoProtocol<T> { var valueWillChange: (MyClass<T>) -> Bool var valueDidChange: (MyClass<T>) -> Void } 

Be especially careful with memory management, because blocks have a strong reference to the object to which they refer. In contrast, delegates are usually weak links to avoid loops.

+2
Feb 19 '15 at 19:44
source share



All Articles