Question

Let's say I have a javascript file, javascript.js, with the following content.

window.fruitsAndVeggies = {
    name2CategoryMap: {
        "apple": "fruit",
        "carrot": "vegetable"
    }
}

Can anyone tell me the easiest way to get the contents of the Javascript object window.fruitsAndVeggies into an NSDictionary?

From various sources on the internet I've pieced together the following snippet, that creates a Javascript context, and then evaluates the javascript code in that context.

JSGlobalContextRef ctx = JSGlobalContextCreate(NULL);  // create context


JSValueRef exception = JSValueMakeUndefined(ctx); // create object to hold exceptions

// Make a "window" object in the JS Context
JSStringRef makeWindowScript = JSStringCreateWithUTF8CString("var window = {}");
JSValueRef result = JSEvaluateScript( ctx, makeWindowScript, NULL, NULL, 0, &exception );


// load the javascript file into an NSString
NSBundle *          bundle = [NSBundle bundleWithIdentifier:@"my.bundle"];
NSString *filePath = [bundle pathForResource:@"javascript" ofType:@"js"];

NSError *error;
NSString *stringFromFile = [[NSString alloc]
                                 initWithContentsOfFile:filePath
                                 encoding:NSUTF8StringEncoding
                                 error:&error];

// convert NSString to a JSStringRef
CFStringRef cfString = (__bridge CFStringRef)stringFromFile;
JSStringRef jsString = JSStringCreateWithCFString(cfString);


// evaluate the javascript
JSEvaluateScript( ctx, jsString, NULL, NULL, 0, &exception );

I'm confused as to what to do next. I need to use the contents of fruitsAndVeggies.name2CategoryMap in my objective-c code. What's the easiest way to access them? Is there a simple function I can call to load them into an objective-c dictionary?

Many thanks for your help.

Was it helpful?

Solution

Things have become a lot simpler since the new Objective-C interface in JavaScriptCore introduced with iOS 7. For an intro to this, see the WWDC 2013 session "Integrating JavaScript into Native Apps" session on Apple's developer network: https://developer.apple.com/videos/wwdc/2013/?id=615

The code you need after evaluating the javascript resource file is:

    JSContext *context = [JSContext contextWithJSGlobalContextRef:ctx];
    NSDictionary *fruitsAndVeggiesDict = [context[@"window"][@"fruitsAndVeggies"][@"name2CategoryMap"] toObject];

    NSLog(@"name2CategoryMap['apple'] = %@", fruitsAndVeggiesDict[@"apple"]);
    NSLog(@"name2CategoryMap['carrot'] = %@", fruitsAndVeggiesDict[@"carrot"]);

OTHER TIPS

Try as I might, I couldn't figure out an elegant way to get window.fruitsAndVeggies into a dictionary. The best I could do was to hand-roll a loop that iterates through the javascript object and adds each key/value pair to a dictionary one-by-one.

The code's below. Be warned: it's ugly. If you've got a more succinct solution, I'm all ears!

/* Make the dictionary. */
NSMutableDictionary *fruitsAndVeggiesDict = [NSMutableDictionary dictionary];


/* In the JS context, create a variable called "keys". 
   Return the number of keys (keys.length).  */
result = JSEvaluateScript( ctx, JSStringCreateWithUTF8CString("var keys = Object.keys(window.fruitsAndVeggies.name2CategoryMap); keys.length"), NULL, NULL, 0, &exception );

double numKeys = JSValueToNumber(ctx, result, &exception);


/* Iterate over the keys; convert each key/value into a pair of NSStrings. */
for(int i = 0; i < numKeys; i++){

    // get a key, save in keyStrNS
    NSString *getKeyStr = [NSString stringWithFormat:@"keys[%d]", i];
    CFStringRef getKeyStrCF = (__bridge CFStringRef)getKeyStr;
    JSStringRef getKeyStrJS = JSStringCreateWithCFString(getKeyStrCF);

    JSValueRef key = JSEvaluateScript( ctx, getKeyStrJS, NULL, NULL, 0, &exception );

    JSStringRef keyStrJS = JSValueToStringCopy(ctx, key, &exception);
    NSString *keyStrNS = (NSString *)JSStringCopyCFString( kCFAllocatorDefault, keyStrJS );


    // get a value, save in valueStrNS
    NSString *getValueStr = [NSString stringWithFormat:@"window.fruitsAndVeggies.name2CategoryMap[\"%@\"]", keyStrNS];
    CFStringRef getValueStrCF = (__bridge CFStringRef)getValueStr;
    JSStringRef getValueStrJS = JSStringCreateWithCFString(getValueStrCF);

    JSValueRef value = JSEvaluateScript( ctx, getValueStrJS, NULL, NULL, 0, &exception );

    JSStringRef valueStrJS = JSValueToStringCopy(ctx, value, &exception);
    NSString *valueStrNS = (NSString *)JSStringCopyCFString( kCFAllocatorDefault, valueStrJS );

    /* store the key and value in our dictionary */
    [fruitsAndVeggiesDict setObject: valueStrNS  forKey: keyStrNS];

    /* and print them for good measure */
    NSLog(@"key %@ has value %@",keyStrNS,valueStrNS);

}

Output:

key apple has value fruit
key carrot has value vegetable
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top