Nathan de Vries

Wielder of bits, bytes & all things nice

Enabling Remote Debugging via Private APIs in Mobile Safari

The WebKit Web Inspector in Safari & Chrome is part of every decent web developer’s toolkit, but unfortunately it’s unavailable in Mobile Safari.

Or is it?

While I was researching my previous article on enabling WebGL on iOS via private WebKit APIs, another API caught my eye while I was class-dumping the private WebKit.framework.

Private WebView API
1
2
3
@interface WebView (WebPrivate)
+ (void)_enableRemoteInspector;
@end

I did a little bit of research and found an article by Pavel Feldman (@repenaxa) on the Surfin’ Safari blog about WebKit’s remote debugging feature. This private +[WebView _enableRemoteInspector] API enables those same features in mobile WebKit.

Note: This API is available as of iOS 5.0. To use it, you’ll need a copy of Xcode with the iOS 5.0 SDK. You’ll also need to make sure that if you’ve got the iOS 4.0 SDK installed alongside the iOS 5.0 SDK, that you’ve selected iOS 5.0 in the iPhone Simulator via the Hardware → Version menu. Hat tip to Phil Oye (@philoye) for pointing that out.

Enabling remote debugging in a UIWebView

To see if I could get it working, I checked out a copy of my UIWebViewWebGL project, and called the private API in application:didFinishLaunchingWithOptions:

Enabling the remote inspector on launch
1
2
3
4
5
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  // ...Snipped...
  [NSClassFromString(@"WebView") _enableRemoteInspector];
  // ...Snipped...
}

The Surfin’ Safari article said the remote debugger runs on port 9222, but connecting to http://localhost:9222 while the app was running in the simulator didn’t seem to work.

Using lsof showed that UIWebViewWebGL.app was actually listening on port 9999:

$ sudo lsof -i TCP -a -p $(pidof UIWebViewWebGL) -P
COMMAND    PID   USER   FD   TYPE             DEVICE SIZE/OFF NODE NAME
UIWebView 3748 nathan    9u  IPv6 0xffffff800fa25d80      0t0  TCP localhost:9999 (LISTEN)
UIWebView 3748 nathan   13u  IPv6 0xffffff80111ba340      0t0  TCP localhost:9999->localhost:52483 (ESTABLISHED)

Now when I pointed my browser at http://localhost:9999, it worked!

Logging document.location.href in the web inspector console shows I’m successfully inspecting the web content from UIWebViewWebGL.app running in the iPhone Simulator.

Huzzah!

Enabling remote debugging in Mobile Safari

Enabling the web inspector in an embedded UIWebView is handy, but what about content running in Mobile Safari?

To do that, we need to call the +[WebView _enableRemoteInspector] API from within the Mobile Safari app. The easiest way to do that is by launching Mobile Safari in the iPhone Simulator, and then use gdb to attach to it and call the private API.

Script to enable the remote inspector in Mobile Safari
1
2
3
4
5
6
7
8
9
10
11
12
13
MobileSafari_PID=$(ps x | grep "MobileSafari" | grep -v grep | awk '{ print $1 }')

if [ "$MobileSafari_PID" == "" ]; then
  echo "Mobile Safari.app must be running in the Simulator to enable the remote inspector."
else
  
  cat <<EOM | gdb -quiet > /dev/null
  attach $MobileSafari_PID
  p (void *)[WebView _enableRemoteInspector]
  detach
EOM

fi

Browsing to http://localhost:9999 will show an index page listing the URL of each tab open in Mobile Safari. This allows you to have a remote inspector open for each tab in Mobile Safari.

Summing Up

I haven’t used the remote inspector in anger yet, but so far it’s been incredibly useful.

Note that you can’t use gdb to inject code on a device, so enabling remote debugging in Mobile Safari is limited to the Simulator. Hopefully Apple will provide the option to enable it, much like the debug console in Settings → Safari → Advanced → Debug Console.

In the meantime, you can load your web content in an embedded UIWebView. Enabling the remote inspector on a UIWebView in an app running on the device doesn’t seem to work. Using nmap shows that my iPhone only has port 62078 open for OTA syncing, so it’s likely there’s a firewall preventing connections to the remote inspector port.

Discuss on Hacker News »