Debugging JavaScriptCore using Safari Web Inspector

With new JavaScriptCore in iOS, we have the ability to add dynamic
scripting to mobile apps.

If you've been working on embedded JavaScript for iOS projects, you
know the pain: you have to compile and install the app, just to test
the JavaScript changes you made. When something goes wrong, you have
to guess the problem blindly, with primitive debugging supports.

How good if we have a console/REPL that let us inspect JavaScript objects
and run it?

I'm not the only one. This WebKit issue
implement related code in JSContext such that we can use Safari
Web Inspector to inspect iOS JSContext. Unfortunately this is quite
new and not included in iOS 7.0 and 7.1.

Web Inspector for UIWebView

The hope is not all lost. Although there is no interface to perform
remote inspecting directly on JSContext, you can do it on a UIWebView.
Following code snippets enable this:

// this is not app store safe, so do not include this in your release build!
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
    [NSClassFromString(@"WebView") performSelector:@selector(_enableRemoteInspector)];
#pragma clang diagnostic pop

Now run your app in simulator and open desktop Safari, in Develop menu
you can connect to your web view and inspect elements inside, include
JavaScript console.


JSContext of UIWebView

But a UIWebView is not a JSContext (even its JavaScript engine is JavaScriptCore).
There are no public interface to access JSContext of the UIWebView.

Luckily impathic found
a way around this:

JSContext *ctx = [webView valueForKeyPath:@”documentView.webView.mainFrame.javaScriptContext”];

Wrap Up

To access your JSContext in Web Inspector, instead of create it with [[JSContext alloc] init],
create a UIWebView get it's JSContext. It work like a regular JSContext, just with additions like
DOM but you can ignore the. Combine with the Web View hacks above, you can open the JSContext
and inspect it on Safari. Here is a demo:


Of course you should wrap all this in a handy class where you only get this debug JSContext in
development build.