Search code examples
iosjavascriptcorejsvalue

How to pass a JS function as object property to iOS


I have a JSExport protocol named ChannelExport with the method - (void)sendRequest:(NSDictionary *)request withCallback:(JSValue *)callback;. Calling this method from Javascript code works alright, like this:

channel.sendRequestWithCallback({'foo': 'bar'}, function(response) { ... });

In ObjC, I can access the values in the request dictionary and also call the callback function.

Now I'd like to change the interface to - (void)sendRequest:(NSDictionary *)request, passing the JS function as part of the request dictionary, like this:

channel.sendRequestWithCallback({
  'foo': 'bar'
  'callback': function(response) { ... }
});

In this case, when I try to call the callback function in ObjC, the app crashes. Apparently the callback object is not a JSValue, but instead an NSDictionary (to be more precise, an __NSDictionaryM). I was assuming that the JS function is correctly wrapped as JSValue in the same way as it is when passing it as a simple parameter.

Any hint why this is happening, and how to solve the issue?


Solution

  • You can't use - (void)sendRequest:(NSDictionary *)request signature to achieve your goal. If you define argument as NSDictionary, JavaScriptCore will recursively convert all objects in this dictionary to corresponding Objective-C objects. Use - (void)sendRequest:(JSValue *)requestValue instead:

    - (void)sendRequest:(JSValue *)requestValue {
        JSValue *fooJSValue = [requestValue valueForProperty:@"foo"];
        NSString *bar = [fooJSValue isUndefined] ? nil : fooValue.toString;
        // use bar
    
        JSValue *callback = [requestValue valueForProperty:@"callback"];
        [callback callWithArguments:myArguments];
    }