13

I have a Cocoa app that uses a WebView to display an HTML interface. How would I go about calling an Objective-C method from a Javascript function within the HTML interface?

The Archetypal Paul
  • 41,321
  • 20
  • 104
  • 134
georgebrock
  • 28,393
  • 13
  • 77
  • 72

4 Answers4

10

This is documented at developer.apple.com.

Mason Bryant
  • 1,372
  • 14
  • 23
Adam Wright
  • 48,938
  • 12
  • 131
  • 152
  • Thanks, somehow missed that in my Google searches :) – georgebrock Sep 18 '08 at 13:25
  • 2
    For anyone searching for iOS help, note that the above answer is Mac-only. – zekel Mar 23 '12 at 17:46
  • 2
    Note that the link has changed, so this link-only answer is not useful anymore. – Steven Fisher Aug 10 '12 at 21:06
  • 5
    The link points to "Using JavaScript from Objective-C", which wasn't the question. This page shows how to call obj-C methods from JavaScript: https://developer.apple.com/library/mac/#documentation/AppleApplications/Conceptual/SafariJSProgTopics/Tasks/ObjCFromJavaScript.html – auco Apr 14 '13 at 21:09
  • 1
    good sample including most of usage scenarios is : CallJS - https://developer.apple.com/library/mac/samplecode/CallJS/Introduction/Intro.html – PetrV Sep 24 '15 at 13:30
4

Being rather green, Apple's documentation is pretty unusable for me, so I made a proof of concept of calling Objective C methods from javascript and vice versa in Cocoa, though the latter was much easier.

First make sure you have your webview as the setFrameLoadDelegate:

[testWinWebView setFrameLoadDelegate:self];

You need to tell the webview to watch for a specific object as soon as it's loaded:

- (void)webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)windowScriptObject forFrame:(WebFrame *)frame {
    //add the controller to the script environment
    //the "ObjCConnector" object will now be available to JavaScript
    [windowScriptObject setValue:self forKey:@"ObjCConnector"];
}

Then the business of the communication:

// a few methods to log activity
- (void)acceptJavaScriptFunctionOne:(NSString*) logText {
    NSLog(@"acceptJavaScriptFunctionOne: %@",logText);
}
- (void)acceptJavaScriptFunctionTwo:(NSString*) logText {
    NSLog(@"acceptJavaScriptFunctionTwo: %@",logText);
}

//this returns a nice name for the method in the JavaScript environment
+(NSString*)webScriptNameForSelector:(SEL)sel {
    NSLog(@"%@ received %@ with sel='%@'", self, NSStringFromSelector(_cmd), NSStringFromSelector(sel));
    if(sel == @selector(acceptJavaScriptFunctionOne:))
        return @"functionOne"; // this is what you're sending in from JS to map to above line
    if(sel == @selector(acceptJavaScriptFunctionTwo:))
        return @"functionTwo"; // this is what you're sending in from JS to map to above line
    return nil;
}

//this allows JavaScript to call the -logJavaScriptString: method
+ (BOOL)isSelectorExcludedFromWebScript:(SEL)sel {
    NSLog(@"isSelectorExcludedFromWebScript: %@", NSStringFromSelector(sel));
    if(sel == @selector(acceptJavaScriptFunctionOne:) ||
       sel == @selector(acceptJavaScriptFunctionTwo:))
        return NO;
    return YES;
}

The key is that if you have multiple methods you'd like to call, you need to have them all excluded in the isSelectorExcludedFromWebScript method, and you need the javascript call to map out to the ObjC method in webScriptNameForSelector.

Full project proof of concept file: https://github.com/bytestudios/JS-function-and-ObjC-method-connector

3

If you wanna do it in iPhone apps, you would need to do a trick with the UIWebViewDelegate method shouldStartLoadWithRequest:

This api http://code.google.com/p/jsbridge-to-cocoa/ does it for you. It is very lightweight.

Terry
  • 1
  • 1
0

I have a solution using NimbleKit. It can call Objective C functions from Javascript.

Adam Lear
  • 38,111
  • 12
  • 81
  • 101
Rony
  • 1,229
  • 4
  • 22
  • 42