Tuesday, November 17, 2020

3 ways to call ObjC methods from swift

 There are three ways to dynamically call the method in this class:

1. Using performSelector()

let selector = NSSelectorFromString("titleForItem:withTag:")
let unmanaged = toolbar.perform(selector, with: "foo", with: "bar")
let result = unmanaged?.takeRetainedValue() as? String

2. Using methodForSelector() with @convention(c)

typealias titleForItemMethod = @convention(c)
    (NSObject, Selector, NSString, NSString) -> NSString
  
let selector = NSSelectorFromString("titleForItem:withTag:")
let methodIMP = toolbar.method(for: selector)
let method = unsafeBitCast(methodIMP, to: titleForItemMethod.self)
let result = method(toolbar, selector, "foo", "bar")

3. Using NSInvocation

It's only available in Objective-C.
SEL selector = @selector(titleForItem:withTag:);
NSMethodSignature *signature = [toolbar methodSignatureForSelector:selector];

NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
invocation.target = toolbar;
invocation.selector = selector;

NSString *argument1 = @"foo";
NSString *argument2 = @"bar";
[invocation setArgument:&argument1 atIndex:2];
[invocation setArgument:&argument2 atIndex:3];

[invocation invoke];

NSString *result;
[invocation getReturnValue:&result];

Or, we can use Dynamic 🎉

let result = Dynamic(toolbar)            // Wrap the object with Dynamic
    .titleForItem("foo", withTag: "bar") // Call the method directly!

More details on how the library is designed and how it works here.