Integrating WebView into a Cocoa app

When using a WebView object inside a Cocoa application, one might think they are limited to only displaying web pages. However, using custom CSS and JavaScript, a WebView object can become a very powerful and flexible part of a Cocoa app, while looking and working natively.

The first thing you should know is that there is plenty of WebKit-specific CSS. Because you’re writing for a WebView object only, you can (thankfully) ignore all other browsers, and thus you get to use all of this WebKit goodness without any worry. For example, your ‘background-color’ can now be set to ‘window’ which is a handy constant telling the browser to render the color the same as the background of a Cocoa window. There’s also -webkit-appearance, which, while a little lacking at the moment, is a great way to make your element look like native Cocoa search fields, buttons, and text fields, among other controls. Another great feature of WebKit is the “webkit-gradient” function, which essentially allows developers and designers to replace custom gradient graphics with brief, flexible, and most importantly, editable, CSS code.

Now that we can utilize WebKit’s specific CSS to get a nice native-looking user interface, we need to make it do something! Enter WebScriptObject. This is a class brought in by WebKit.framework, and is used in Cocoa. Normally the only WebScriptObject that Cocoa developers should touch is the one received by calling -[WebView windowScriptObject]. This gives you a handle on your WebView’s scripting object, and once you call this, you can do two very important things:

First of all, you can call JavaScript functions from inside your Cocoa app. You do this by calling -evaluateWebScript or -callWebScriptMethod:withArguments: on your WebView’s -windowScriptObject. This will return a value if your JS function does so, or at the very least, executes a JS function inside your WebView. This is handy for, say, refreshing information inside your WebView.

Secondly, and probably more importantly, you can set new objects that your JavaScript scripts inside your WebView can call functions (methods) on. We do this with basic KVC. For example, calling [scriptObject setValue:self forKey:@"ControllerObject"] will give the JavaScript environment a new top-level object called ControllerObject. Now, you can call Cocoa methods from inside JavaScript. There’s only two gotcha, but they can be a little elusive at first:

The first gotcha is naming conventions. If your Cocoa object implements -method, then method() is the function you can call on it. Pretty simple, right? But what if your method is -method:withObject:? Like in the other scripting bridges, simply replace colons with underscores. This, that method becomes method_withObject_(). Kind of lame, I agree, but if it really bugs you can implement +webScriptNameForSelector: which is found in WebScripting informal protocol.

The second gotcha is related to that protocol I just brought up for the first time. See, any object that lives inside your JavaScript environment, implicitly conforms to WebScripting, except it usually doesn’t implement any of these optional methods. There’s two very important methods you’ll have to implement before your JS code can call any Cocoa:

+ (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector
+ (BOOL)isKeyExcludedFromWebScript:(const char *)name

Implement these and return NO for both, while you’re testing. Otherwise, your Cocoa calls from inside JS will never be executed! I recommend that once you’re out of development phase, you make these methods conditional and only allow methods/keys that you call from inside your JS code, for security purposes.

Yes, it takes a little bit of work, but in the end, you have a WebView that looks beautiful and native, and actually interacts with the rest of your app!

Related posts:

  1. Integrating iSight into your cocoa application… Having iSight integrated into you app can be useful in...
  2. Bottom Bars in Cocoa Mac OS X 10.5 Leopard introduced a new user...

One Response

[...] reading this blog, you may want to check out this very detailed blog on exactly how to integrate Cocoa into your WebView so you have a better understanding of exactly how this process works. It’s not difficult, but [...]

Leave Your Response

* Name, Email, Comment are Required