Monday, December 30, 2013

How to debug live PhoneGap apps

PhoneGap is a powerful tool for cross-platform mobile application development. You can debug the applications you develop with PhoneGap using a web browser or a smartphone emulator, but eventually you have to test your apps on actual devices. Doing this poses new challenges, because on these platforms you don't have access to your usual web development tools. Here are some ways to overcome this problem and debug your live app in a real user environment.
We'll be working with Android tools, because iOS developers have it easier: up to iOS 5 they could use Weinre (as we'll do; see below) or iWebInspector to remotely run a debug session. From iOS 6 onward they can use the included-by-default official Web Inspector, which obsoletes the other tools.
For testing, I wrote a simple application that allows a user to enter a city and country. It then shows the current weather for that location as provided by the Open Weather Map API. If the specified city isn't found, or if the user didn't type in a city, the application displays an alert. I created the application using PhoneGap's command-line interface, and just replaced the index.html with one of my own. I included in the code many logging messages.
The running app

The local way

Programmers usually include logging statements in their programs so they can follow the path of code execution. From JavaScript, you can generate log entries with statements such as console.log(...) and console.assert(...). A running PhoneGap application logs the output from these statements internally, and you can view them from a console. Not all console methods are available in the PhoneGap internal browser, however:
console.log("a.string") or"a.string") Outputs the given message to the console. It is shown as an information-level message.
console.warn("a.string") Outputs the given message as a warning, meaning a potential problem.
console.error("a.string") Outputs the given message as an error – a definite problem.
console.dir(some.object) This doesn't work (you just get a fairly useless "[object Object]" description). Check Douglas Crockford's code and write console.log(JSON.stringify(some.object)) instead.
console.assert(a.condition, "a.string") If the given condition is false, output the message as an error; otherwise, do nothing.
With Android, you could switch back and forth from your app to a console, using something like ConnectBot, to check the output of the logcat command. With a rooted phone, you can use grep as provided by BusyBox. Alternatively, you could use the catlog app, but for Android 4.1 and later, you need a rooted phone to use it.
Catlog can show you all console output from your app.
An easier way to get at your log output uses adb, the Android Debug Bridge. Connect your smartphone to your PC and type adb shell | grep -i "web con" to see the filtered output of the log:
I/Web Console( 9938): onLoad - fired at file:///android_asset/www/index.html:24
I/Web Console( 9938): onLoad - exiting at file:///android_asset/www/index.html:26
I/Web Console( 9938): onDeviceReady - fired at file:///android_asset/www/index.html:30
W/Web Console( 9938): Alert Inform: Ready to work! at file:///android_asset/www/index.html:18
I/Web Console( 9938): onDeviceReady - exiting at file:///android_asset/www/index.html:32
I/Web Console( 9938): getWeather button clicked at file:///android_asset/www/index.html:36
I/Web Console( 9938): getWeather - location: new york city, usa at file:///android_asset/www/index.html:42
I/Web Console( 9938): getWeather - URL: at file:///android_asset/www/index.html:46
I/Web Console( 9938): getWeather - AJAX call done at file:///android_asset/www/index.html:48
I/Web Console( 9938): showWeather - all OK, status 200 at file:///android_asset/www/index.html:54
I/Web Console( 9938): getWeather button clicked at file:///android_asset/www/index.html:36
I/Web Console( 9938): getWeather - location: qwertyuiop at file:///android_asset/www/index.html:42
I/Web Console( 9938): getWeather - URL: at file:///android_asset/www/index.html:46
I/Web Console( 9938): getWeather - AJAX call done at file:///android_asset/www/index.html:48
W/Web Console( 9938): showWeather - unexpected status 404 at file:///android_asset/www/index.html:63
W/Web Console( 9938): Alert Problem: Couldn't get data - check the city and country at file:///android_asset/www/index.html:18
You may want to try adb's interesting filters. In any case, with adb you can dynamically inspect all the logging your app produces, so you won't be working in the dark.
Of course working with logs and nothing else isn't so great, so let's try out a web inspector that can go beyond mere text messages.
Beware the Heisenbugs!
Heisenbugs (a portmanteau word that comes from Heisenberg, the physicist who first asserted that observing an event affects it, and "bug") are the worst kind of bugs – they seem to disappear whenever you try to find them. When you modify your program so you can debug it you can inadvertently change the conditions that lead to the bug. For example, you may modify timing conditions, or change a variable's value, and thereby make the error go away – until you remove your debugging code only to see the bug come up again! Weinre and similar tools can cause this weird behavior. Be warned, and don't give up looking if an error appears to be solved when you start debugging it.

The remote way

The local debugging method we've been seeing doesn't require modifying your source code (other than including logging statements) but doesn't lend itself to answering on-the-spot questions such as "what value does that variable have?" or "what did that function return?" In web development, you have access to all sorts of developer tools, but when you are running the app in a smartphone, you don't – but there's a solution!
Weinre, which stands for Web Inspector Remote, is a debugger designed to work remotely, over the web. It allows you to debug your PhoneGap app, which, after all, is just HTML code running on a special browser. Weinre runs on Node.js (for more conventional uses of Node.js, see Using Node.js to write RESTful web services). Installation is simple, requiring a mere sudo npm install -g weinre command. Weinre has many configuration runtime options, but you can do with weinre --boundHost -all-, which just starts Weinre running, listening to all possible clients.
WeinreThe main page for a running Weinre instance, ready for clients to connect to it.
To enable Weinre debugging, inject in your index.html page before compiling it into an app and uploading it to a device. Then, open http://your.own.server:8080/client/#your.code in your browser, fire up the app in the device, and you'll be able to run a debugging session at your browser.
Weinre connectedA remote app has connected to a Weinre server.
Useful Weinre tabs include Console, which shows you a full console, and Elements, which provides access to the underlying document. The figure below shows the output on the Console tab, which matches what we saw earlier above.
Weinre consoleThe Console tab shows all console output, and also lets you work with JavaScript.
In the console you can also execute JavaScript code (see the last line above), examine variables, and more, as if you were debugging an app at your own browser. The Elements tab lets you work with the DOM and examine all the elements and styles in your page.
Weinre ElementsThe Elements tab lets you dynamically inspect your web page.
Check the documentation for more on the other tabs: Resources, having to do with local storage; Network, dealing with XmlHttpRequest calls to servers (no other calls are shown); and Timeline, showing time spent. If you already use similar tools you'll feel right at home with Weinre.

The PhoneGap Build way

If you don't care to install Weinre at your server, you can make do by using PhoneGap's own debug service: Enter an ID, inject an script in your index.html file, and click on the given link to start debugging by using a remote Weinre instance.
PhoneGap debugPhoneGap also provides its own Weinre instance if you don't want or have one of your own.
You can also configure the PhoneGap Build service to inject the required script on its own, so when you build your app it will be ready for remote debugging: Click on your app, then click on Settings, and check the Enable Debugging box.
PhoneGap configYou can configure the PhoneGap Build Service to enable debugging on its own.
This method can cause a performance hit, but you can use it anywhere, outside your development network, so it's worth considering.

In conclusion

One last point: If you develop with Google Web Toolkit and you've read my previous articles on using GWT with PhoneGap – Create mobile apps easily with GWT and PhoneGap and Using GWT and PhoneGap for complex mobile applications – you'll be happy to hear that you can use the methods in this article with GWT+PhoneGap apps.
When it comes to debugging your PhoneGap app in the real world, you have several tools at your disposal to remotely check your code. Give them a try for your next project!

No comments:

Post a Comment