14

This is in response to this blog:

http://blog.bignerdranch.com/3784-javascriptcore-and-ios-7/

Thoughts from the iOS devs on SO?

Chad Adams
  • 1,319
  • 1
  • 9
  • 14
  • Recommend this library, you can communicate between javascript and objc easily with UIWebView: https://github.com/liaojinxing/HybridBridge – liaojinxing Mar 26 '14 at 02:50

5 Answers5

25

You can get a JSContext from a UIWebView with a key path:

UIWebView *webView = [[UIWebView alloc] init];
JSContext *ctx = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
ctx[@"document"][@"body"][@"style"][@"background"] = @"steelblue";

Apple never got around to documenting any of the new JavaScriptCore APIs, so I'm not sure if this counts as an internal/undocumented API or not. I have an app approved that uses this method.

Update: Another alternative solution suggested at https://github.com/TomSwift/UIWebView-TS_JavaScriptContext is to make a category on NSObject and use it to implement WebKit's documented didCreateJavaScriptContext delegate callback. To paraphrase that implementation, you can just call [NSObject contextForWebView:myWebView] to grab the JSContext for a UIWebView:

@implementation NSObject(JSContextTracker)

+ (NSMapTable *)JSContextTrackerMap {
    static NSMapTable *contextTracker;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        contextTracker = [NSMapTable strongToWeakObjectsMapTable];
    });
    return contextTracker;
}

- (void)webView:(id)unused didCreateJavaScriptContext:(JSContext *)ctx forFrame:(id)alsoUnused {
    NSAssert([ctx isKindOfClass:[JSContext class]], @"bad context");
    if (!ctx)
        return;
    NSMapTable *map = [NSObject JSContextTrackerMap];
    static long contexts = 0;
    NSString *contextKey = [NSString stringWithFormat:@"jsctx_%@", @(contexts++)];
    [map setObject:ctx forKey:contextKey];
    ctx[@"JSContextTrackerMapKey"] = contextKey; // store the key to the map in the context itself
}

+ (JSContext *)contextForWebView:(UIWebView *)webView {
    // this will trigger didCreateJavaScriptContext if it hasn't already been called
    NSString *contextKey = [webView stringByEvaluatingJavaScriptFromString:@"JSContextTrackerMapKey"];
    JSContext *ctx = [[NSObject JSContextTrackerMap] objectForKey:contextKey];
    return ctx;
}

@end
marcprux
  • 9,845
  • 3
  • 55
  • 72
  • 11
    Has anyone else used either of these methods and had their app accepted by Apple? – Matthew Gertner Apr 08 '14 at 16:46
  • How was the key path on the web view discoverable? I see a `_documentView` getter in the runtime headers, but I'm curious how anyone dug further than that. – ray Sep 04 '14 at 04:20
8

I came up with an approach to get the UIWebView JSContext that is different from the KVC approach.

Basically, by knowing some details about WebKit, we can implement a delegate callback method on NSObject and be handed the JSContext when it is created. Details here:

https://github.com/TomSwift/UIWebView-TS_JavaScriptContext

TomSwift
  • 39,369
  • 12
  • 121
  • 149
  • Excellent code sample. I wasn't aware that for the methods to be called they need to be part of a JSExport protocol – codrut Mar 08 '16 at 09:08
5

It'd be useful for running non-web-related JavaScript hosted inside your application. Think about if you had a bunch of existing code written in JavaScript that you didn't want to re-write? You can use JavaScriptCore without a UIWebView to host that code in your process. I can also imagine it being used to add user-scriptability to iOS apps. The possibilities are endless!

The other thing worth mentioning here is that UIWebView is very resource-hungry (after all, it's like running a copy of Safari in your process); It allocates a TON of memory that you will simply never get back. If you don't specifically need web rendering, JavaScriptCore can do a lot with a lot fewer resources. See my answer over here for details about UIWebView's resource consumption.

Community
  • 1
  • 1
ipmcc
  • 29,581
  • 5
  • 84
  • 147
  • Would the ability to run arbitrary JS scripts, contributed by users, get an app rejected from the app store? – Danyal Aytekin Oct 04 '13 at 16:13
  • I am not an expert on AppStore rules, but my understanding is that if the user types it in, then you can run it, but you can't download code over the network. See http://pythonforios.com for an example of this. – ipmcc Oct 04 '13 at 16:16
3

You can implement application logic in JavaScript and used the JS code on iOS and other platforms. This cuts down the work of supporting an app on iOS, Android, Web, etc.

Calatrava is a framework around this idea.

Community
  • 1
  • 1
ChucK
  • 2,114
  • 1
  • 18
  • 20
-3

You can write ios7 apps using JavaScript:

http://www.youtube.com/watch?v=y-nodF6Cp1Y

d13
  • 9,817
  • 12
  • 36
  • 44
  • 1
    Don't care, I do this now with JavaScript I want native Obj-C code injecting JavaScript without the garbage collection of JavaScript. – Chad Adams Mar 26 '14 at 03:35