Debugging JavaScript on Android and iOS

Debugging JavaScript on Android and iOS

Overall, mobile browsers are an appealing alternative to native applications: They support most modern web standards like HTML5 and CSS3, and you don't have to set up and learn using multiple SDKs. The technical limitations that do exist  will hopefully get fewer as time goes on (or get patched otherwise), while performance and stability will improve. Sure, native apps will always have some advantages, but proper debugging tools should not be one of them. Why those were not available for iOS and Android right from the start is baffling to me, it basically threw us back into the stone age of alert-debugging.

[ Want to develop native apps in JavaScript? Try tabris.js: tabrisjs.com. ]

In this article I will describe four basic approaches to debugging JavaScript on Android (stock browser and Google Chrome) and iOS (Safari), and consider the pros and cons respectively. While you can read about some of these tools here and there, I've got the impression that some authors only tried them for a short time to see if it works at all. I really used most of these at some point to get actual work done.

I will occasionally use an example script that contains the following function:

<pre lang="javascript">
function foo( myObject, myNumber ) {
  var bar = 10 / myNumber;
  var result = myObject.calcSomething( bar );
  document.getElementById( "result" ).innerHTML = "The result is " + result;
}
</pre>
It is expected to be called with the second parameter (myNumber) being 7. It is then supposed to write "The result is 286" on the screen. The function itself has no bugs in it, but it might not be working for a number of other reasons.

Contents:

  1. Native Tools
  2. JavaScript and Bookmarklets
  3. HTTP Remote-Debugging
  4. USB Remote-Debugging
  5. Conclusion

1. Native Tools

This is what you can do with only your phone or tablet. (Without injecting additional JavaScript into your application.) It isn't much.

iOS

For iOS you can enable the debug console. Go to Settings -> Safari -> Advanced, set "Debug Console" to "on". This makes a bar appear at the top of the browser. Tapping that bar shows the console. Here all uncaught exceptions are logged, much like they would on MacOS/Windows Safari for example. The buttons at the bottom allow you to filter the messages, but you can not type anything, or interact in any other way.

This would not really explain why our script does not print anything.

You can also use console.log() in your script to print out messages:

Now we can tell that it's myObject that is null, but only since we already suspected it and put the log there.
It also does not tell us why. For that a stacktrace, or better yet, a step-by-step debugger would be helpful.

The functions debug(), info(), warn() and error() do the same, just with different icons. There are also a bunch of other functions on the console object, but they all don't seem to do anything. Even console.trace(), which can be really useful sometimes, doesn't work at all.

I also found an JS console app, but it's more suitable for experimenting with JavaScript, not for debugging.

Android

The Android Browser doesn't have a console, or any other tools working out of the box. However, I found some apps in Googles Play store that could help.

JS Log is basically a replacement for the js iOS console. It logs messages and uncaught exceptions in the Android notification area. Too bad that it worked very unreliably.  On my Android 3 tablet it sometimes didn't log even when explicitly calling console.log(). On my Android 4 phone it didn't work at all. (So no Google Chrome support either.) Not recommended.

The HTML DOM Viewer is a minimalstic browser (presumably using the default Android browser internally) that also features a console (for logging only), and allows you to inspect the current HTML/DOM state. Unlike JS Log it's pretty reliable, but it's not very convenient to use. I wasn't able to scroll or clear the console, which is already a deal-breaker. The HTML view has no syntax highlighting at all, and longer tags do not wrap, nor can you scroll horizontally. Oh, and it has ads:

Sadly, no real recommandation for this tool either.

Summary Native Tools:

Pro:

  • Quick Setup
  • Still better than using "alert"

Con:

2. JavaScript and Bookmarklets

Debug-tools can also be implemented with JavaScript itself, and they can be surprisingly powerful. Of coure, running in the same enviorement as the application that they are supposed to be debugging bears a slight risk of interference.

D.I.Y.-Console for Android

The minimal requirement for me is to have a (reliable) way to log errors and messages. So I wrote this little script for Android browsers:

<pre lang="javascript">
if( ( /android/gi ).test( navigator.appVersion ) ) {
  console = {
    "_log" : [],
    "log" : function() {
      var arr = [];
      for ( var i = 0; i < arguments.length; i++ ) {
        arr.push( arguments[ i ] );
      }
      this._log.push( arr.join( ", ") );
    },
    "trace" : function() {
      var stack;
      try {
        throw new Error();
      } catch( ex ) {
        stack = ex.stack;
      }
      console.log( "console.trace()\n" + stack.split( "\n" ).slice( 2 ).join( "  \n" ) );
    },
    "dir" : function( obj ) {
      console.log( "Content of " + obj );
      for ( var key in obj ) {
        var value = typeof obj[ key ] === "function" ? "function" : obj[ key ];
        console.log( " -\"" + key + "\" -> \"" + value + "\"" );
      }
    },
    "show" : function() {
      alert( this._log.join( "\n" ) );
      this._log = [];
    }
  };

  window.onerror = function( msg, url, line ) {
    console.log("ERROR: \"" + msg + "\" at \"" + "\", line " + line);
  }

  window.addEventListener( "touchstart", function( e ) {
    if( e.touches.length === 3 ) {
      console.show();
    }
  } );
}
</pre>

Copy this code into your application or put it in an external .js file and load it from your application. It replaces the dummy "console" object of the Android or Chrome browser with one that actually works. You can see the console either by calling console.show() form your code, or by touching the document with three fingers at once. (So be sure to remove the code before your application goes live.) It obviously won't work on devices that don't have multi-touch.

In contrast to the other console implementations up to this point it also supports "trace" and "dir" commands.

As you can see, it uses the alert function to display the log. This keeps the code small, robust and minimally invasive. It works well enough since Android's alert-window supports scrolling. However, if you wanted to, you could easily replace it with a

popup and pretty-print the output, or add other features like javascript execution. Personally, I like this lightweight, simplistic approach. I did not have any problems with it yet, except that the lines tend to wrap a lot due to the small size of the popup - especially on phones. It does not (and isn't supposed to) work on iOS.

Firebug Lite

Firebug was one of the first debugging add-ons for browsers, and it's name is almost synonymous for  such tools. Firebug Lite implements a subset of the Firebug features using JavaScript. For the most part it works well on iPad and Android tablets, though it's clearly not made for touchscreens. To use it with your application, add the following line in your header:

<textarea rows="2" style="margin: 0px; height: 48px; width: 100%; "><script type="text/javascript" src="https://getfirebug.com/firebug-lite-debug.js"></script></textarea>

This is the so-called debug version, because for some reason the minified version didn't work for me anywhere but the iPad. Of course this only works if you have an internet connection. Otherwise you have to download Firebug Lite and host it yourself.

Firebug will appear automatically:

Firebug Lite on the iPad. Finally a stack-trace in Safari!

You can view scripts, but not set a breakpoint. However, it is possible to use the console to execute JavaScript commands:

 You should disable the keyboards auto-uppercase feature, it will save a lot of time while typing.

Viewing HTML is possible, but a bit fiddly because the UI is not designed for touchscreens. Hitting the "+" icon can be an ordeal.

This would explain why the text isn't visible...

The DOM tab doesn't work for some reason. One possible improvement with this approach is to attach a keyboard and/or mouse to your tablet.

If your Android device has an USB port you can also attach a normal mouse.

Of course you can also just pinch-zoom in (when your application allows it), but it's less convinient and may produce minor glitches. Apparently no one has tried to make a touchscreen-friendly version of firebug or something similar. (Well, excluding my selfmade console above.)  I'm sure there are other tools like this out there, but I didn't find any really worthwhile yet.

On iPhone it didn't work at all for some reason, but Firebug is pretty much impossible to operate on phones anyway, due to the small screen.

Bookmarklets

Adding JavaScript tools to your application by editing your HTML/JS files isn't very convenient, and you risk forgetting to remove them afterwards. An alternative to this are bookmarklets. These are bookmarks that contain JavaScript that is executed in the loaded website. They can be created from any javascript with tools like this one. On desktop browser they are usually added using drag and drop, but that doesn't work on mobile devices. One method is to add them to your desktop browser and sync them to your phone/tablet. (I haven't tried that yet.) Alternatively, you have to do this:

First you need to somehow get the code (starting with "javascript:") into the iOS/Android clipboard. Bookmarklets on websites are usually given as a link, so that won't work. Copy the code on your PC/Mac and put it into a textfile, or email, or whatever you like, and get that to your device. For our examples you can just use the code below. Select (long-press) and copy it.

Firebug Lite (debug):

<textarea wrap="off" rows="1" style="margin: 0px; height: 60px; width: 100%; ">javascript:(function(F,i,r,e,b,u,g,L,I,T,E){if(F.getElementById(b))return;E=F[i+'NS']&&F.documentElement.namespaceURI;E=E?F[i+'NS'](E,'script'):F[i]('script');E[r]('id',b);E[r]('src',I+g+T);E[r](b,u);(F[e]('head')[0]||F[e]('body')[0]).appendChild(E);E=new%20Image;E[r]('src',I+L);})(document,'createElement','setAttribute','getElementsByTagName','FirebugLite','4','firebug-lite-debug.js','releases/lite/debug/skin/xp/sprite.png','https://getfirebug.com/','#startOpened');
</textarea>

Firebug Lite (minified):

<textarea  wrap="off" rows="1" style="margin: 0px; height: 60px; width: 100%; ">javascript:(function(F,i,r,e,b,u,g,L,I,T,E){if(F.getElementById(b))return;E=F[i+'NS']&&F.documentElement.namespaceURI;E=E?F[i+'NS'](E,'script'):F[i]('script');E[r]('id',b);E[r]('src',I+g+T);E[r](b,u);(F[e]('head')[0]||F[e]('body')[0]).appendChild(E);E=new%20Image;E[r]('src',I+L);})(document,'createElement','setAttribute','getElementsByTagName','FirebugLite','4','firebug-lite.js','releases/lite/latest/skin/xp/sprite.png','https://getfirebug.com/','#startOpened');
</textarea>

Android Console:

<textarea  wrap="off" rows="1" style="margin: 0px; height: 60px; width: 100%; ">javascript:(function(){if((/android/gi).test(navigator.appVersion)){console={"_log":[],"log":function(){var%20arr=[];for(var%20i=0;i<arguments.length;i++){arr.push(arguments[i]);}this._log.push(arr.join(",%20"));},"trace":function(){var%20stack;try{throw%20new%20Error();}catch(ex){stack=ex.stack;}console.log("console.trace()\n"+stack.split("\n").slice(2).join("%20%20\n"));},"dir":function(obj){console.log("Content%20of%20"+obj);for(var%20key%20in%20obj){var%20value=typeof%20obj[key]==="function"?"function":obj[key];console.log("%20-\""+key+"\"%20->%20\""+value+"\"");}},"show":function(){alert(this._log.join("\n"));this._log=[];}};window.onerror=function(msg,url,line){console.log("ERROR:%20\""+msg+"\"%20at%20\""+"\",%20line%20"+line);}window.addEventListener("touchstart",function(e){if(e.touches.length===3){console.show();}});}})();
</textarea>

On the iPad, you first  have to create a bookmark in the share menu, then edit it using the "Edit" button in the bookmarks menu. Paste the code where the URL would be. For the Android browser it's a bit simpler, since you can edit the URL while creating a new bookmark. Depending on the Android version/browser, bookmarks are added by pressing some star or "+" icon, or by selecting "Save to bookmarks" from a menu.

iOS on the left, Android on the right. Obviously.

Now you just have to open the bookmark while your application is running. This works very well to open Firebug Lite on the iPad, or the DIY console on Android 3. Sadly, that's it. I had no success with Android 2 and 4 at all, and Android 3 wouldn't load Firebug half of the time, or only very slowly. Another problem is that if you need the tool while the document is loading (e.g. for logging), this solution won't work.

Summary JavaScript and Bookmarklets:

Pro:

  • Stack trace on all browser.
  • Somewhat satisfying HTML view and JS execution on tablets.
  • You can easily write your own tools (within limits) if you want.
  • Reasonably fast setup...

Con:

  • ...except for bookmarklets, which don't even work everywhere.
  • No step-by-step debugging, profiling or request log. DOM exploration not working in FB.
  • Limited usability and performance, especially for tools not designed for mobile devices.
  • Potentially invasive, risk of interfering with target application.

3. HTTP Remote-Debugging

To get around the limitations of a touchscreen-only device, remote debugging is (besides an emulator) the only way to go. In this chapter we look at tools that achieve this by injecting JavaScript into your application (debug target) that communicates with an HTTP-server (debug server), and can then be controlled by another browser (on your desktop, debug client). I will only explore the scenario of a one-to-one connection.

weinre

Probably the best known of such tools is weinre. Similar to Firebug Lite, it implements a subset of the features of the WebKit Developer Tools. It has now become part of the Apache Cordova project, but there are no official releases of that yet (August 2012).  However, you can get weinre "unofficially" here. I'm using "weinre-jar-1.6.1.zip" on a windows machine in my example.

Unzip the jar file, then type on your console (or create a script or shortcut):

<textarea rows="1" style="margin: 0px; height: 28px; width: 100%; ">java -jar weinre.jar --boundHost -all- --httpPort 9090</textarea>

It should print "weinre:HTTP server started at [...]". Then Add the following line to your HTML , with [...] standing in for your IP:

<textarea rows="2" style="margin: 0px; height: 28px; width: 100%; "><script src="http://[...]:9090/target/target-script-min.js#anonymous"></script>
</textarea>

You can also use a bookmarklet (you can get the code at "http://localhost:9090/"), but if you have a dynamic IP it's pretty pointless.

Then open Safari or Google Chrome on your desktop machine and go to "http://localhost:9090/client/#anonymous". There your mobile device should appear under "Targets" as soon as you load your application.

This is what it should look like. When you reload your target application,
multiple targets may be displayed temporarily.

Weinre has a similar functional range as Firebug Lite. It doesn't support as much console commands (no trace for example), but it features a network and a timeline tab. (The last one is very limited though, and doesn't seem to catch touch events.) No step-by-step debugging or profiling either, those would likely by impossible to implement in JavaScript.

The Network tab is useful if you work with XMLHttpRequests, like this Eclipse RAP application.

I had the occasional problem to get a working connection, and performance can be bad if your application is very complex. Also, hardcoding an IP address in your script is obviously not a good idea, especially if your IP may change often. Which is where we get to...

Adobe Shadow

While the name might make you think Adobe Shadow is completely different tool developed entirely by Adobe Systems, this is not the case. It has some nice little features of its own (like taking a screenshots of a website on multiple devices at once), but for our purposes it's just a convenient wrapper around weinre.

The difference is that you don't have to add any javascript to your application yourself. Instead, you download an app (here for iOS and here for Android), which will do that for you. You will also need the server component (no Linux version though) and a Google Chrome extension.

After you have all that, start the Adobe Shadow application and Google Chrome on your desktop, and the app on your device. Supposedly the app should find the server by itself, but I always had to enter the IP manually. Then you have to enter a short pin from your device on the Chrome extension, and you're connected. Your connected devices will all go to the same URL as your desktop Chrome browser, which is cool in theory, but can get really weird/annoying if you just change tabs to quickly look something up.

Now you just have to click the "<>" icon besides any of your listed devices, and a popup with weinre opens.

I have to say that Adobe Shadow isn't much of an improvement over weinre. In fact, with Shadow I had a lot more issues with not getting or even loosing a connection than with just weinre. Also, both Adobe Shadow apps (iOS+Android) have that feature where they reload the page without using cache (useful!) - triggered by a "pull-down" gesture. This can interfere with your application if you work with touch events or vertically scrolling elements. It really annoys me because it's so unnecessary. Dear Adobe, what's wrong with using a button? It's a tool, not a videogame!

I say go with weinre, unless your IP changes really often. And even then, consider that you still have to re-connect the devices (with a pin!) to your desktop machine every time. Also, Shadow does not work on linux.

Other

Socketbug and Firebug Crossfire and remote jsconsole take the same basic approach as weinre. I didn't get around to try them, but they seem to offer less functionality and no significant improvement over weinre. Perhaps one of these tools will come out on top eventually, but for now I recommend weinre.

Summary HTTP Remote-Debugging:

Pro:

  • Works on all browser i tested.
  • weinre/A.S can log network activity.
  • Better usability than previous methods...

Con:

  • ...once the connection is established, which can be a hassle every time.
  • Still no step-by-step debugger or profiler. Timeline very limited.
  • Only minimal console support. (e.g. no stack trace)
  • Noticeable delays possible.
  • No linux support for Adobe Shadow.
  • Still invasive. (Runs partially in same environment as target.)

4. USB Remote-Debugging

This is very similar to HTTP Remote Debugging, but communicating with the browser application itself, not some JavaScript running inside.

Android

This is only possible with Google Chrome, not the default browser. (Chrome is available for Android 4+).

First it is required to enable USB debugging on the phone/tablet: Go to the device settings, select "Developer options" and enable "USB debugging". Then open Chrome (still on the mobile device), open the menu, go to Settings -> Developer tools -> "Enable USB Web debugging".

For the PC(/Mac) a bit of setup is requried. Download and install the Android SDK, specifically the "platform tools" and "Google USB Driver". It's available for Windows, Mac and Linux. Now connect your device to your PC via USB.  In the SDK-directory "platform-tools" you can find the executable "adb". To check that the device is properly connected, type "adb devices". It should appear in the printed list.

Now type "adb forward tcp:9222 localabstract:chrome_devtools_remote" and open "localhost:9222" on your PC's Google Chrome. You'll see a gallery representing the tabs opened on the mobile Google Chrome. Select one and you get the usual WebKit Developer Tools. Like with weinre you can use the console and DOM exploring, but now we can finally do step-by-step debugging:

No more trial and error logging, the debugger reveals everything.

Reloading the page on the device does not require a re-connect, and even breakpoints are sustained. There's no noticeable delay.

The Timeline tab works much better now, and there is even more stuff like Profiling.

Because I'm lazy I wrote a little script so I can always get the developer tools opened in a few seconds.

Windows batchfile:

<textarea rows="6" style="margin: 0px; width: 100%; ">@echo off
adb devices
adb forward tcp:9222 localabstract:chrome_devtools_remote
%localappdata%\Google\Chrome\Application\chrome.exe "http://localhost:9222/"
pause</textarea>

If your device gets listed here, but the desktop Chrome shows something like "This webpage is not available", then USB Debugging is probably not enabled on the mobile Chrome.

iOS

For iOS there is a tool called iWebInspector, which seems to be very similar. However, it's only working with the iOS simulator, not with real devices. More importantly, it's only running properly on Mac OS 10.6 or 10.7, but no later. (Windows and Linux are out anyway.) As such, I couldn't test it and consider it useless by now.

However, it's very likely Apple does not want to stay behind Google for long, so an alternative is arriving soon. Time will tell if it's going to be a match for Google Chrome's remote debugging. Support for Windows or Linux machines doesn't seem very likely though. Bad Apple!

EDIT: From iOs 6 on remote-debugging is supported. However, you need Safari 6 on your desktop machine, which is only available for Mac OS X Lion or later. Even worse, the mobile Safari JavaScript console has been removed, making your life that much harder if you're a Windows or Linux user.

Summary USB Remote-Debugging:

Pro:

  • All features you could ask for!
  • Mostly stable and fast connection.
  • Non-invasive.

Con:

  • Initial setup may be a bit time-consuming.
  • No support for Android stock browser.
  • Currently no good solution for iOS. Future solution will likely exclude win/linux users., unless you have a current Mac.

5.Conclusion

The main issue here is that there is not one solution that fits for all cases and all browsers. The only option I can really recommend wholeheartedly is the remote USB debugging of Chrome, but that's no solution if you have a problem with another browser. If you require only logging you are probably going to be covered, but anything beyond that requires sub-optimal tools like weinre or Firebug Lite. Of those two I can't generally recommend one over the other. See which one best fits your needs.

You can also use a user agent switcher (like this one) to test your application in a normal browser. This works fine as long as you don't have a browser-specific problem, or need any devices-specific features for your testing (like touch events, rotation, etc.).

I can also generally recommend  to write unit tests and run them on your mobile devices. That already gives you a lot more security while writing more code. It  may be worth another blog post entirely.

If someone has anything to add, please let me know in the comments. I'm more than happy to update the article. I'll also try to keep it up to date regarding new releases like iOS 6 or Apache Cordova.

No Comments

Sorry, the comment form is closed at this time.