JayData.org

Debugging a PhoneGap application

Author: Daniel Jozsef August 1st, 2012

, , ,


When I had my first run-in with PhoneGap, I was furious with the lack of debugging facilities. I mean sure, XCode provides adequate tools for debugging Objective-C code, but the JavaScript running in the UI window cannot be debugged.

The official documentation says that if you wish to debug your UI code, you should load it as a standalone in a browser. Of course this has certain drawbacks. One is that the UIWebView, the iOS component that PhoneGap uses to display your UI, behaves slightly differently than the mobile Safari, and even more differently than the desktop one. The other problem is that you won’t have access to the native PhoneGap plugins.

If your application is complex enough, it will be a single, complex entity, with native and JavaScript code leaning on and intertwining each other. That is what has happened with our ebook reader too, the books were extracted from their containers by the native code. The interdependence was simply far too high to debug the JavaScript as a standalone.

We were having trouble with a blocking issue that did not appear in unit testing of either the JS or the native code. We were on the verge of giving up, when we found out about the…

Secret UIWebView debugger!

The iOS simulator included with XCode in fact contains a javascript debugger and DOM inspector (basically the Safari developer tools) for UIWebViews, but it is unreleased. There is no checkbox or menu item you can use to activate it. However, you can enable it from code through a secret ninja technique.

We are going to reach behind the public API. Neither the class nor its method are published through header files, so we will use the NSClassFromString function to get there. If you are familiar with the concept of reflection from managed languages, this is something similar. It gives you access to a class based on a name given in a string.

This is the line you need to run when your application starts. The best place to put it is in the “didFinishLaunchingWithOptions” method in AppDelegate.m. (You can find this file in the Classes folder of your project.)

Note that the compiler will issue a warning, since the method you use is not declared in a header you included. However, due to the “duck typing” nature of Objective-C, the code will compile, and since the method does exist, will run correctly.

1-Running

Now what this does is slightly obscure, as you will find no changes from before when running the application. You need a little ninja finesse even to access the debug facilities. What has happened is that the iOS simulator has opened a port for access by a browser.

Open localhost:9999 on a browser, and you will be presented with a rather plain selection between the UIWebViews present in your application. If you are using PhoneGap, chances are there will be only one.

2-select

After clicking it, you will be taken to a developer tools window, similar to the one built into Safari. If you have debugged JavaScript in a browser already, you will be familiar with how this works.

3-debugger

Happy debugging… Just make sure to remove this code from your app before submitting it to the App Store, or else it will certainly be rejected without fail, for using nonpublic calls.

The PhoneGap DebugView

Another “secret” debugger is the DebugView included with PhoneGap. It dumps verbose debug messages on the output console while running. Note that this should also be removed from the application before submitting it for review.

First, you need to locate the code, which is NOT included by default with the new PhoneGap project. Navigate to the PhoneGap library root (for me the installer created it at Documents/CordovaLib), where you will find a folder named “debugview” under “Classes“.

Take this folder, and copy it under your project. If you wish to abide by Objective-C conventions, this belongs under the Vendor folder, as it is third-party code you integrate as source into your project.

4-debugview

However, just compiling it into the project isn’t enough, you will need to use it to replace the default PhoneGap view object. You can do this by overriding the newCordovaViewWithFrame method in MainViewController.h, as shown in the image above.

You need the following code:

Note that in the image above, I defined a preprocessor variable for turning this on and off. The result? It dumps each and every JavaScript exception and some other debug data to the output window. This can be mighty misleading, so be careful! Not all exceptions are errors, many JavaScript libraries use handled exceptions to determine browser capabilities, for example.

Example snippet of its output:

To be honest, we didn’t benefit much from using this component, but in certain cases, it can most certainly be useful. What did help us a LOT however, was…

Using the PhoneGap event handlers to catch errors

When developing the book reader, we were faced with an inexplicable error upon an iFrame being opened. Debugging through the JavaScript didn’t yield any result, and neither did placing breakpoints in the plugin code.

What eventually solved our problem was the event handlers in MainViewController.m. This class contains the event handlers for the application, even though most of them are commented out by default, as the handler implementations themselves reside in the superclass.

Uncommenting these methods can serve two purposes. Either you wish to implement custom logic, or you wish to place a breakpoint. And this is what we did. The method names speak for themselves: webViewDidStartLoad, webViewDidFailLoadWithError, etc.

Placing breakpoints here, you can follow the very heartbeat of the PhoneGap application, and catch any error that occurs outside the JavaScript code, or the plugin code you are more familiar with.


, , ,