4

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?

Hamish
  • 78,605
  • 19
  • 187
  • 280
yageek
  • 4,115
  • 3
  • 30
  • 48
  • Possibly related: [Protocol doesn't conform to itself?](https://stackoverflow.com/questions/33112559/protocol-doesnt-conform-to-itself). – Martin R Jul 10 '17 at 12:02
  • Do you want to check if `meta` is strictly equivalent to `MyProtocol`, or is equivalent or derives from `MyProtocol`? – Hamish Jul 10 '17 at 13:17
  • I would like to test if meta is compatible/conforms to `MyProtocol` – yageek Jul 10 '17 at 13:43

1 Answers1

4

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
Hamish
  • 78,605
  • 19
  • 187
  • 280