Stampy

Dev Blog

Remotely Debugging JavaScript in iOS

This post continues our series on Stampy Day, a company-wide hack day here at Paperless Post. We hope you enjoy reading about some of the projects that came out of that day.

Last year we set out on an ambitious project to phase out our interactive flash based “create tool”, replacing it with JavaScript and HTML5. The platform we were initially targeting wasn’t the web, but instead our native iOS app. This was a move that would allow us share a common code base and feature set across all platforms.

The “Viewport”, as we call it, is written entirely in JavaScript, and was headed up by @talltyler. On the iOS sides of things, we immediately found that the Apple provided UIWebView which is capable of executing JavaScript wasn’t the right tool for the job (for numerous reason). We were fortunate enough to find the open source project Ejecta, which is a self described “Fast, Open Source JavaScript, Canvas & Audio Implementation for iOS.”

During development, the majority of JavaScript debugging occurred at the browser level, using the debug console in chrome and safari. This often left things to chance when we ran them in iOS. For Stampy Day this year, we decided to address that issue by building a remote debug-console for iOS + JavaScript. On the web side of things, @talltyler built a web app that we run on our Mac Mini build server. The web app uses sockets via Socket.io, and can host multiple clients simultaneously. On the iOS side of things, we are also using a Cocoa implementation of Socket.io to connect with the server. Once a connection has been established, you can access any device from the web app and start debugging! Debugging works just like you would expect: type in some javascript, send it, and BOOM, fresh console output.

On iOS we created a special subclass of our “javaScriptView” that is instantiated in debug mode as a cluster class. Once you have the viewport up and running, connecting is easy:

1
2
3
4
5
6
- (void)openConnection
{
    self.remoteConnection = [PPJSRemoteConnection connectionWithDelegate:self];
    [self.remoteConnection setHost:self.host];
    [self.remoteConnection openConnection];
}

Whenever the web app sends in JavaScript, our delegate receives a callback, evaluates the JS, and returns the result if a callback is requested

1
2
3
4
5
6
7
8
9
- (void)remoteConnection:(PPJSRemoteConnection *)connection didReceiveMessage:(PPJSRemoteMessage *)message requestedCallBack:(void (^)(NSArray *args))callback
{
  NSString *js = [message javaScriptToBeEvaluated];
  //debug_eval has the JSContext execute the provided JavaScript and return a value, if any 
    id returnValue = [self debug_eval:js]? :  [NSNull null];
    if (callback) {
        callback(@[returnValue]);
    }
}

Thanks to the dynamic nature of JavaScript, we are able to diagnose issues, update code, and fix bugs in real-time without the need to rebuild the iOS app or JavaScript file.

Web app remote debugger

iOS Viewport

Comments