Why use JavaScriptCore in iOS7 if it can't access a UIWebView's runtime?
Asked Answered
F

5

14

This is in response to this blog:

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

Thoughts from the iOS devs on SO?

Fideliafidelio answered 20/9, 2013 at 15:31 Comment(1)
Recommend this library, you can communicate between javascript and objc easily with UIWebView: github.com/liaojinxing/HybridBridgeUnsure
T
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
Tildy answered 19/11, 2013 at 1:19 Comment(2)
Has anyone else used either of these methods and had their app accepted by Apple?Fluxion
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.Kauri
H
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

Helotry answered 22/11, 2013 at 23:10 Comment(1)
Excellent code sample. I wasn't aware that for the methods to be called they need to be part of a JSExport protocolTheft
N
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.

Noguchi answered 20/9, 2013 at 16:33 Comment(2)
Would the ability to run arbitrary JS scripts, contributed by users, get an app rejected from the app store?Thunderhead
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 pythonforios.com for an example of this.Noguchi
U
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.

Ugly answered 29/9, 2013 at 12:59 Comment(0)
Z
-3

You can write ios7 apps using JavaScript:

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

Zagreus answered 8/12, 2013 at 14:49 Comment(1)
Don't care, I do this now with JavaScript I want native Obj-C code injecting JavaScript without the garbage collection of JavaScript.Fideliafidelio

© 2022 - 2024 — McMap. All rights reserved.