Description
In general, there are two reasons that may prevent you from using a particular Swift class or method in Objective-C.
First, the pure Swift class uses a C ++-style vtable dispatcher, which is not understood as Objective-C. This can be overcome in most cases by using the dynamic
keyword, as you obviously understand.
Second, as soon as generics are generated, Objective-C loses the ability to see any methods of the generic class until it reaches a point in the inheritance hierarchy where the ancestor is not shared. This includes newly introduced methods as well as overrides.
class Watusi : NSObject { dynamic func watusi() { println("watusi") } } class Nguni<T> : Watusi { override func watusi() { println("nguni") } } var nguni = Nguni<Int>();
When passed to Objective-C, it effectively perceives our nguni
variable as an instance of Watusi
, and not an instance of Nguni<Int>
, which it does not understand at all. Passed by nguni
, Objective-C will print "watusi" (instead of "nguni") when the Watusi
method is Watusi
. (I say “effective” because if you try this and type the class name in Obj-C, it shows _TtC7Divided5Nguni00007FB5E2419A20
, where Divided
is the name of my Swift module. Therefore, ObjC certainly “knows” that this is not Watusi
.)
Bypass
A workaround is to use thunk, which hides the type parameter. My implementation differs from you in that the generic parameter is an observable class, not a key type. This should be considered as one step above the pseudo-code and is not well smoothed (or well thought out) beyond what is needed in order to get you the gist. (However, I checked it.)
class Subscriber : NSObject { private let _observe : (String, AnyObject, [NSObject: AnyObject], UnsafeMutablePointer<Void>) -> Void required init<T: NSObject>(obj: T, observe: ((T, String) -> Void)) { _observe = { keyPath, obj, changes, context in observe(obj as T, keyPath) } } override func observeValueForKeyPath(keyPath: String, ofObject object: AnyObject, change: [NSObject : AnyObject], context: UnsafeMutablePointer<Void>) { _observe(keyPath, object, change, context) } } class Publisher: NSObject { dynamic var string: String = nil } let publisher = Publisher() let subscriber = Subscriber(publisher) { _, _ in println("Something changed!") } publisher.addObserver(subscriber, forKeyPath: "string", options: .New, context: nil) publisher.string = "Something else!"
This works because Subscriber
itself is not generic, but only its init
method. Closures are used to “hide” a generic type parameter from Objective-C.