I'm looking for a way to match dynamically an Objective-C Protocol
instance with a corresponding Swift protocol.
I have a protocol defined in swift that is compatible with Objective-C:
@objc(YHMyProtocol) protocol MyProtocol { }
I try to perform the match, in a function:
public func existMatch(_ meta: Protocol) -> Bool {
// Not working
if meta is MyProtocol {
return true
}
// Not working also
if meta is MyProtocol.Protocol {
return true
}
return false
}
This function is intended to be called from an Objective-C file:
if([Matcher existMatch:@protocol(YHMyProtocol)]) {
/* Do Something */
}
The existMatch
function always returns false.
I can not figure out how to solve this. Did I miss something in the implementation?
Protocol
is an opaque object type. It's defined in the generated header as:
// All methods of class Protocol are unavailable.
// Use the functions in objc/runtime.h instead.
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0)
@interface Protocol : NSObject
@end
It doesn't conform to MyProtocol
, so is MyProtocol
cannot work. And, although Swift can implicitly bridge @objc
protocol metatypes to Protocol
, it appears that it cannot do the reverse; which is why is MyProtocol.Protocol
doesn't work (but even if it did, it wouldn't work for derived protocols; as P.Protocol
types can currently only hold the value P.self
).
If you want to check that meta
is a protocol type that is equivalent to, or derives from, MyProtocol
, you can use the Obj-C runtime function protocol_conformsToProtocol
:
@objc(YHMyProtocol) protocol MyProtocol { }
@objc protocol DerviedMyProtocol : MyProtocol {}
@objc class Matcher : NSObject {
@objc public class func existMatch(_ meta: Protocol) -> Bool {
return protocol_conformsToProtocol(meta, MyProtocol.self)
}
}
// the following Swift protocol types get implicitly bridged to Protocol instances
// when calling from Obj-C, @protocol gives you an equivalent Protocol instance.
print(Matcher.existMatch(MyProtocol.self)) // true
print(Matcher.existMatch(DerviedMyProtocol.self)) // true
If you just want to check that meta
is equivalent to MyProtocol
, you can use protocol_isEqual
:
@objc class Matcher : NSObject {
@objc public class func existMatch(_ meta: Protocol) -> Bool {
return protocol_isEqual(meta, MyProtocol.self)
}
}
print(Matcher.existMatch(MyProtocol.self)) // true
print(Matcher.existMatch(DerviedMyProtocol.self)) // false