Call a JS function from Obj-C and pass an Obj-c callback/function? #197

SRandazzo opened this Issue Jun 26, 2013 · 11 comments


None yet

4 participants


When calling a function on JS from Objective-c, is there anyway to pass an objective-c block/callback into JS?

for example:

[self.jsView evaluateScript:@"myAsyncFunction(123, myCallback())"];
guileen commented Jun 27, 2013

You can take a look #193


Thanks but #193 looks like it's talking about executing JSCallbacks from Obj-c (If i'm understanding it correctly)

I want to pass an objective-c block/callback into JS and let the JS execute it


In your objc binding:

EJ_BIND_FUNCTION(callFunction, ctx, argc, argv) {
    JSObjectRef jsCallback = (JSObjectRef)argv[0];

    JSObjectRef objcCallback = [scriptView createFunctionWithBlock:^JSValueRef(JSContextRef ctx, size_t argc, const JSValueRef *argv)
        NSLog(@"block called from JS with value: %@", JSValueToNSString(ctx, argv[0]));
        return NULL;

    JSValueRef params[] = { objcCallback };
    [scriptView invokeCallback:jsCallback thisObject:NULL argc:1 argv:params];
    return NULL;

In JavaScript:


This should help a bit, I hope :)

@phoboslab phoboslab closed this Jul 9, 2013


createFunctionWithBlock:^JSValueRef(JSContextRef ctx, size_t argc, const JSValueRef *argv)


whoaaaaa, awesome! thanks a ton, this is great


@phoboslab this is really great so far, but how can i execute this from outside the scope of a JS function being called?

For example from anywhere in my obj-c code If i want to tell the js view
[self.jsView evaluateScript:@"doSomething()" params:jsParams];


Not sure I understand. You want to call the JS function doSomething() and pass in an obj-c block function?

If doSomething() is global (which it is, if you can invoke it with evaluateScript), you can get a reference to it like this:

JSObjectRef global = JSContextGetGlobalObject(ctx);

JSStringRef jsFuncName = JSStringCreateWithUTF8CString("doSomething");
JSObjectRef jsFunc = (JSObjectRef)JSObjectGetProperty(ctx, global, jsFuncName, NULL);

// ...
[scriptView invokeCallback:jsFunc thisObject:NULL argc:1 argv:params];

(The ScriptView's invokeCallback method should probably be renamed to invokeFunction, or something)


Very helpful! Seems like we could even wrap some of that up in Obj-C

While it works if I call the function doSomething that i have implemented, when i try to use it on other global objects, for example PP.api.doSomething it does not execute :-/

If it helps, I'm also using the jsview.jsGlobalContext as the ctx, so:

JSObjectRef global = JSContextGetGlobalObject(  self.jsView.jsGlobalContext  );

JSStringRef jsFuncName = JSStringCreateWithUTF8CString("PP.api.doSomething");
JSObjectRef jsFunc = (JSObjectRef)JSObjectGetProperty( self.jsView.jsGlobalContext , global, jsFuncName, NULL);

Invoking that above does nothing, in my js i have:

API.prototype.doSomething = function () {
    console.log('DO SOMETHING');
    return null;

and then we have a

PP.api = new API({.....

PP.api.doSomething is not a global object, PP is. api is a property of PP and doSomething is a property of api. You have to call JSObjectGetProperty for each name recursively.

I'd propose a method like this on the scriptView (untested):

- (JSValueRef)getValueForPath:(NSString *)objectPath {
    JSValueRef obj = JSContextGetGlobalObject( jsGlobalContext  );

    NSArray *pathComponents = [objectPath componentsSeparatedByString:@"."];
    for( NSString *p in pathComponents) {
        JSStringRef name = JSStringCreateWithCFString((CFStringRef)p);
        obj = JSObjectGetProperty( jsGlobalContext, (JSObjectRef)obj, name, NULL);

        if( !obj ) { break; }
    return obj;

JSObjectRef func = (JSObjectRef)[scriptView getValueForPath:@"PP.api.doSomething"];

Let me know how that goes; this method would probably be a good addition to Ejecta.


That works perfect!

Definitely a worthwhile addition to the project, really rounds out the communication from Objc-C to JS!

thanks again for all the help! I can gladly PR back changes if you like

finscn commented Jul 17, 2013

I think it's named getValueByPath better than getValueForPath ;)

@phoboslab phoboslab added a commit that referenced this issue Jul 18, 2013
@phoboslab Added jsValueForPath. See #197 290930b

Renamed the method to jsValueForPath. The "for" is more consistent with Apple's API names, e.g. NSDictionary valueForKey.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment