1

I have an objc method I'd like to call from swift, but the method exists in a third party framework that may or may not exist at runtime. What I'd like to do is call it dynamically, so I'm looking into interacting with it using selectors.

The method signature looks like this in ObjC

- (NSString * _Nullable)buildData:(NSError * _Nullable __autoreleasing * _Nullable)error;

Removing the nullable annotations, it's the bog standard "return a thing, or error" pattern that ObjC has had forever.

- (NSString*)buildData:(NSError*)error;

If swift can load this at compile time, then it quite happily translates into

func buildData() throws -> String

However, I'd like to call it dynamically. I've worked out I can do this:

let _target:NSObject = // obtain a reference to the underlying value

var error: NSError? = nil
_target.perform(NSSelectorFromString("buildData:"), with: &error)

The problem is, I can't pass a reference to the perform selector method. XCode gives me a compile error of

'&' used with non-inout argument of type 'Any?'

So my question is, how can I call this method using selectors?

Orion Edwards
  • 121,657
  • 64
  • 239
  • 328
  • This might help: https://stackoverflow.com/a/33391876/1187415. – Martin R Sep 06 '20 at 11:57
  • There seems to be a general problem with importing optional “throwing” methods from Objective-C to Swift: https://bugs.swift.org/browse/SR-11676. – Martin R Sep 06 '20 at 13:55

1 Answers1

1

Try this:

var error: NSError? = nil
let _target: NSObject = // obtain a reference to the underlying value

withUnsafeMutablePointer(to: &error) {
    let selector: Selector = NSSelectorFromString("buildData:")
    let methodIMP: IMP! = _target.method(for: selector)
    unsafeBitCast(methodIMP,to:(@convention(c)(Any?,Selector,OpaquePointer)->Void).self)(_target,selector,OpaquePointer($0))
}

More info on using convention(c) for invoking selectors in Swift in my answer here

Kamil.S
  • 5,205
  • 2
  • 22
  • 51