The web scripting capabilities of WebKit permit you to access Objective-C properties and call Objective-C methods from the JavaScript scripting environment.
An important but not necessarily obvious fact about this bridge is that it does not allow any JavaScript script to access Objective-C. You cannot access Objective-C properties and methods from a web browser unless a custom plug-in has been installed. The bridge is intended for people using custom plug-ins and JavaScript environments enclosed within WebKit objects (for example, a WebView).
How to Use Objective-C in JavaScript
A Sample Objective-C Class
The WebScripting informal protocol, defined in WebScriptObject.h
, defines methods that you can implement in your Objective-C classes to expose their interfaces to a scripting environment such as JavaScript. Methods and properties can both be exposed. To make a method valid for export, you must assure that its return type and all its arguments are Objective-C objects or basic data types like int
and float
. Structures and non object pointers will not be passed to JavaScript.
Method argument and return types are converted to appropriate types for the scripting environment. For example:
JavaScript numbers are converted to NSNumber objects or basic data types like int
and float
.
JavaScript strings are converted to NSString objects.
JavaScript arrays are mapped to NSArray objects.
Other JavaScript objects are wrapped as WebScriptObject instances.
Instances of all other classes are wrapped before being passed to the script, and unwrapped as they return to Objective-C.
Let’s look at a sample class. In this case, we will create an Objective-C address book class and expose it to JavaScript. Let’s start with the class definition:
@interface BasicAddressBook: NSObject { |
} |
+ (BasicAddressBook *)addressBook; |
- (NSString *)nameAtIndex:(int)index; |
@end |
Now we’ll write the code to publish a BasicAddressBook
instance to JavaScript:
BasicAddressBook *littleBlackBook = [BasicAddressBook addressBook]; |
id win = [webView windowScriptObject]; |
[win setValue:littleBlackBook forKey:@"AddressBook"]; |
That’s all it takes. You can now access your basic address book from the JavaScript environment and perform actions on it using standard JavaScript functions. Let’s make an example showing how you can use the BasicAddressBook
class instance in JavaScript. In this case, we’ll print the name of a person at a certain index in our address book:
function printNameAtIndex(index) { |
var myaddressbook = window.AddressBook; |
var name = myaddressbook.nameAtIndex_(index); |
document.write(name); |
} |
You may have noticed one oddity in the previous code example. There is an underscore after the JavaScript call to the Objective-C nameAtIndex
method. In JavaScript, it is called nameAtIndex_
. This is an example of the default method renaming scheme in action.
Unless you implement webScriptNameForSelector
to return a custom name, the default construction scheme will be used. It is your responsibility to ensure that the returned name is unique to the script invoking this method. If your implementation of webScriptNameForSelector
returns nil
or you do not implement it, the default name for the selector will be constructed as follows:
Any colon (“:”) in the Objective-C selector is replaced by an underscore (“_”).
Any underscore in the Objective-C selector is prefixed with a dollar sign (“$”).
Any dollar sign in the Objective-C selector is prefixed with another dollar sign.
The following table shows example results of the default method name constructor:
Objective-C selector | Default script name for selector |
---|---|
setFlag: | setFlag_ |
setFlag:forKey:withAttributes: | setFlag_forKey_withAttributes_ |
propertiesForExample_Object: | propertiesForExample$_Object_ |
set_$:forKey:withDictionary: | set$_$$_forKey_withDictionary_ |
Since the default construction for a method name can be confusing depending on its Objective-C name, you would benefit yourself and the users of your class if you implement webScriptNameForSelector
and return more human-readable names for your methods.
Getting back to the BasicAddressBook, now we’ll implement webScriptNameForSelector
for our nameAtIndex
method. In our BasicAddressBook class implementation, we’ll add this:
+ (NSString *) webScriptNameForSelector:(SEL)sel |
{ |
... |
if (sel == @selector(nameAtIndex:)) |
name = @"nameAtIndex"; |
return name; |
} |
Now we can change our JavaScript code to reflect our more logical method name:
function printNameAtIndex(index) { |
var myaddressbook = window.AddressBook; |
var name = myaddressbook.nameAtIndex(index); |
document.write(name); |
} |
For security reasons, no methods or KVC keys are exposed to the JavaScript environment by default. Instead a class must implement these methods:
+ (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector; |
+ (BOOL)isKeyExcludedFromWebScript:(const char *)name; |
The default is to exclude all selectors and keys. Returning NO for some selectors and key names will expose those selectors or keys to JavaScript.
See WebKit Objective-C Framework Reference for all the information on excluding methods and properties from the JavaScript environment.
© 2004, 2008 Apple Inc. All Rights Reserved. (Last updated: 2008-10-15)