FastDev for iOS: How It Works

Some days ago I published a hack that allows patching the Titanium Mobile SDK for enabling fast on-device testing of iOS applications, without needing to rebuild, sign and redeploy them.

With this post I’d like to share some of the inner workings of the Titanium sdk that make that hack possible. I also try to explain in more detail how the hack works, so you may want to check out its source code at https://github.com/omorandi/TiiOSFastDev.

First, I need to say that, in contrast to what many developers believe when they first approach Titanium Mobile, the JavaScript files that compose an application are not compiled to native code. Instead they get interpreted by a JavaScript engine: a slightly modified version of JavaScriptCore on iOS (i.e. the engine used by WebKit), and Rhino (which will be hopefully soon replaced by V8) on Android.

When deploying an application on the iOS emulator, the JS engine gets fed directly with the original files that reside in the Resources directory of your app project, loaded at runtime. This allows you for example to see the changes you made in one file, by just restarting the app in the emulator.

On the other hand, when deploying on the device, the original source files are processed and packed into a single file, just before building the app. This step generates the build/iphone/Classes/ApplicationRouting.m file, which is one of the very few that are dynamically generated by the build process. Indeed, most of the objective-c files that compose the native app are simply copied from the appropriate Ti SDK directory (e.g. /Library/Application\ Support/Titanium/mobilesdk/osx/1.8.0) into the build/iphone directory of the project by the titanium build scripts when you launch the app from Ti Studio, or from the command line.

Once generated, ApplicationRouting.m contains the definition of a dictionary object that associates the name of each JS file present in the Resources directory of your project with a binary representation of its original content, along with a single class method named resolveAppAsset: whose only purpose is to return the appropriate file content for a given path:

ApplicationRouting.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
+ (NSData*) resolveAppAsset:(NSString*)path;
{
    static NSMutableDictionary *map;
    if (map==nil)
    {
        map = [[NSMutableDictionary alloc] init];
        //here the entry for app.js is created
        //(I filled the hex string with garbage - it's only an example)
        [map setObject:dataWithHexString @"DEADBEEF…" forKey:@"app_js"];
    }

    //here the content of the file at "path" is returned to the caller
    return [map objectForKey:path];
}

In other words, this portion of code represents the point where the original content of each JS file is dispatched to the Titanium runtime environment, which will then feed it to the JSCore interpreter.

As you surely understand at this point, the FastDev hack is made possible by simply using a modified version of the resolveAppAsset method, which, instead of returning static data, opens a connection to an HTTP server running on the development machine for retrieving the original JS files from the Resources directory:

ApplicationRouting.m (modified version)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
+ (NSData*) resolveAppAsset:(NSString*)path;
{
    NSString *ServerAddr = SERVER_ADDRESS;
    NSString *ServerPort = SERVER_PORT;
    NSString *path_url = [NSString stringWithFormat:@"http://%@:%@/%@",ServerAddr, ServerPort, path];

    NSURL *url = [NSURL URLWithString:path_url];
    ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
    [request startSynchronous];
    if ([request responseStatusCode] != 200)
        return nil;

    NSError *error = [request error];
    if (error)
        return nil;

    return [request responseData];

}
  1. Since the method is called synchronously, also the HTTP request must be performed in a synchrounous way:

    [request startSynchronous];

  2. For testing the application on the iOS emulator, the request to the server can be made on localhost, however, for on-device testing, we need to know in advance the IP address at which we the server can be reached, because the modified ApplicationRouting.m will be built into the native application running on the target device. This is achieved through a simple trick in the startserver.py script:

startserver.py
1
2
3
4
5
6
7
8
9
ip_addr = "localhost"

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
    s.connect(("gmail.com",80))
    ip_addr = s.getsockname()[0]
except socket.error, msg:
    s.close()
    s = None

This code allows discovering the local IP address that routes to the Internet (specifically to gmail.com), so I make the assumption that the iPhone/iPad used for testing and the development machine reside both on the same local network and that the IP address of the Mac machine on that network is also the one that routes it to the Internet.

So the startserver.py script uses this information for patching the build/iphone/Classes/ServerAddr.h file containing the SERVER_ADDRESS define, which is used in ApplicationRouting.m for creating the HTTP request. This is also why the startserver.py script needs to be launched before building the app.

That’s it!

Titanium Hack: FastDev for iOS

Testing large Titanium Mobile applications on iOS devices can be cumbersome, since the build & deploy process can take several minutes, even when the project is launched directly from XCode. Repeating the same process each time, for checking the effect of small modifications to the code is then quite annoying and time consuming.

I just posted a small project on github at https://github.com/omorandi/TiiOSFastDev that aims at solving this issue.

It is a quite hackish solution that allows you to change on the fly the application resource files of your project and get them pulled by your app (either on device or simulator) without needing to rebuild it, sign it, and re-deploy it.

The solution consists in a bunch of scripts that perform these operations:

  1.  patch your Titanium SDK folder (or, better, a copy of it)

  2. start an HTTP server in the Resources directory of your Titanium Mobile project

Once these steps are completed, you can build your app and deploy it to the device for testing. Should you find you need to fix some of your program files, just do it, manually restart the app, and proceed with testing, without needing to rebuild & redeploy it.

This process is similar to the one involved using the fastdev feature for Android, however the mechanism is not managed by Titanium Studio, and involves a series of steps to be performed manually.

The Ti SDK patching script expects you to work with a 1.8.X Titanium Mobile SDK. It can be modified in order to work with 1.7.X versions, however this is not currently supported.

The http server is implemented as a simple node js script, so for executing it you need a working node environment in your system (you can grab it from http://nodejs.org/ ).

How to use it

First, check out the TiiOSFastDev repository  from github.

In the following steps I use the following symbols for the involved directories:

$TI_FASTDEV_DIR (the directory containing this repository)

$TI_SDK_DIR (usually /Library/Application\ Support/Titanium)

$PRJ_DIR (The root directory of your Ti Mobile App project)

  1. Check out your Titanium Mobile directory under $TI_SDK_DIR. There you’ll find one or more directories of the SDKs installed in your system.

  2. Create a copy of the SDK directory you want to patch and call it for example 1.8.0-fastdev (actually you’ll want to keep the original SDK directory intact for the normal development workflow)

  3. In a terminal window, go to the directory that contains the files of this repository ($TI_FASTDEV_DIR) and perform the following:

    ./patch.sh 1.8.0-fastdev

  4. Launch your Ti Mobile application from Titanium Studio or through your preferred method. This is needed in order to re-create the build/iphone directory in your project, with the patched files. If this doesn’t happen, just clean the project and relaunch it

  5. For your convenience, create an alias for the startserver.pyscript:

    alias fdstart='$TI_FASTDEV_DIR/startserver.py'

  6. Then cd in the Resources directory of your Titanium Mobile application project, for example:

    cd $PRJ_DIR/Resources

  7. Using the previously created alias, you can start the server with:

    fdstart

  8. That’s it, you can now restart the application from Ti Studio (if you want to use the simulator), deploy it on the device, or, better, build the app for device in XCode (by opening the XCode project you find in $PRJ_DIR/build/iphone, sign the product and install it on device. From now on your application JS files are pulled through the server you started in the Resources directory of your project.

  9. Enjoy the time you have saved with this hack ;-)

Demo video

I made a demo for showing how this hack can be used. Please note that when launching the project from XCode I select the Simulator target only for convenience (it’s not that easy to show the iphone screen output in a screencast):

Titanium Mobile iOS FastDev hack demo from Olivier Morandi on Vimeo.

Tracing Objective-c Message Calls

Developing native modules for extending Titanium Mobile is fun, but at times it can be quite frustrating, because of the lack of adequate documentation. The module development guides available in the Appcelerator documentation wiki only show the tip of the iceberg, and while exposing functionality through simple proxy classes is quite straightforward, things can become quickly complicated when implementing ViewProxies, i.e. those that represent view objects to be shown onscreen.

Actually, I’ve been mostly successful creating self contained native views, however problems started arising when I wanted to add Titanium intrinsic ViewProxies (e.g. Ti.UI.View) to my custom native views in iOS. In particular, something I’m still struggling to understand is the exact life cycle of TiViewProxy and their companion TiUIView objects, especially for what concerns the management of layout properties: since Titanium view objects are rendered lazily, there is a quite convoluted (at least at my eyes) mechanism in place for rearraging the layout of whole view hierarchies, which is managed by the TiViewProxy class.

Since Titanium is an open source project, we can always take a look at how things are implemented. Indeed, I think that reading the code of the SDK is a good way to really understand how Titanium Mobile works under the hood, and I plan to share some of my findings in some future post. However, reading source code can be a highly time consuming practice. Also stepping through the code with the debugger can help in some cases, but the risk is to lose a higher level view.

Since I wanted a more “dynamic” look at what happens to my viewproxy objects I thought that tracing the exact sequence of message calls involved between the creation and the onscreen rendering of a simple view could help. By googling around I’ve found some useful resources on the topic here and in the Mac OS X Debugging Magic guide from Apple. It tuns out that this is a feature that can be enabled in any objective-c development environment (i.e. either for MacOS, or iOS targets) by setting the NSObjCMessageLoggingEnabled environment variable to YES in XCode before running the project. In XCode 4 this can be easily done through the "Product->Edit Scheme" menu, by selecting the "Arguments" tab and adding a new environment variable (under the "Environmnet Variables" section), as shown in the following picture:

Then we can run the project, and all the message calls handled by objective-c objects (and classes, for static methods) are logged into a file named /tmp/msgSends-<pid>, where pid is the process ID.

The result is quite overwhelming and looks like an immense sequence of lines like the followings:

+ NSObject NSObject initialize

+ NSRecursiveLock NSObject initialize

+ NSRecursiveLock NSObject new

+ NSRecursiveLock NSObject alloc

+ NSRecursiveLock NSRecursiveLock allocWithZone:

- NSRecursiveLock NSRecursiveLock init

+ NSRecursiveLock NSObject new

+ NSRecursiveLock NSObject alloc

+ NSRecursiveLock NSRecursiveLock allocWithZone:

- NSRecursiveLock NSRecursiveLock init

+ NSLock NSObject initialize

+ NSLock NSObject new

+ NSLock NSObject alloc

+ NSLock NSLock allocWithZone:

- NSLock NSLock init

+ NSLock NSObject new

+ NSLock NSObject alloc

+ NSLock NSLock allocWithZone:

- NSLock NSLock init

+ NSLock NSObject new

[...]

Now, what remains to do is to filter this file, searching for instances of TiWindowProxy, TiViewProxy and TiUIView, for later inspection:

$ grep 'TiWindowProxy\|TiViewProxy\|TiUIView' /tmp/msgSends-31489 > call-seq.txt

and the gems begin to surface:

+ TiViewProxy NSObject initialize

+ TiWindowProxy NSObject initialize

- TiUIWindowProxy TiViewProxy init

- TiUIWindowProxy TiWindowProxy _configure

- TiViewProxy TiProxy _configure

- TiUIWindowProxy TiViewProxy _initWithProperties:

[…]

- TiUIWindowProxy TiWindowProxy open:

- TiUIWindowProxy TiWindowProxy open:

- TiUIWindowProxy TiWindowProxy isModal:

- TiUIWindowProxy TiWindowProxy argOrWindowProperty:args:

- TiUIWindowProxy TiWindowProxy isFullscreen:

- TiUIWindowProxy TiWindowProxy argOrWindowProperty:args:

- TiUIWindowProxy TiWindowProxy openOnUIThread:

- TiUIWindowProxy TiWindowProxy isRootViewAttached

- TiUIWindowProxy TiViewProxy view

- TiUIWindowProxy TiViewProxy viewWillAttach

- TiUIWindowProxy TiWindowProxy newView

- TiUIWindowProxy TiViewProxy appFrame

+ TiUIView NSObject initialize

- TiUIWindow TiUIView touchesBegan:withEvent:

- TiUIWindow TiUIView setFrame:

- TiUIWindow TiUIView proxy

+ TiViewProxy NSObject class

- TiUIWindow TiUIView setProxy:

I need to say that extracting useful information from this sequence is not that easy. However my plan is to follow it as a “map” while stepping through the code with the debugger.

Unfortunately I have no results to show yet. Just stay tuned for a follow up post where I hope I’ll be able to layout at least a portion of the TiViewProxy object lifecycle.

Using Intents for Sending Sms Messages on Android With Ti Mobile

Titanium mobile historically lacks of support for in-app sms sending facilities. Even the availability of the MFMessageComposeViewController class since the release of iOS 4.X didn’t push Appcelerator guys to provide an appropriate mapping in the framework API. I personally tried to overcome the issue (at least on iOS) by creating a custom native module, which had a quite good response from the community.

After releasing the iOS module I started investigating how I could provide a similar feature also for Android devices. It turns out that programmatically sending text messages on Android can be performed in a couple of ways:

  1. by using the android.telephony.SmsManager class
  2. by using the default SMS app through intents

Talking about native code, the simplest way for doing it is the latter, and all can be done with at most 4 lines of code. Since the Titanium SDK provides a direct mapping on native Android intents from release 1.5.0, implementing the same technique in a Ti Mobile project is quite straightforward:

    var intent = Ti.Android.createIntent({

        action: Ti.Android.ACTION_VIEW,

        type: 'vnd.android-dir/mms-sms'

    });

    intent.putExtra('sms_body', 'new message from me');

    intent.putExtra('address', '123456789');

    Ti.Android.currentActivity.startActivity(intent);

What this snippet of code does is simply to create a Ti.Android.Intent object with the appropriate properties and start an activity for it. As a result, the SMS sending activity registered in the system (i.e. probably that of the default SMS messaging app) gets started, pre-populated with the provided recipient (address) and message body (sms_body). That’s it.

This solution is very simple and powerful, with the only drawback that there’s no possibility to get notified about the result of the operation, so we won’t know if the message has been actually sent or not. If in some cases this doesn’t represent a big issue, it’s highly probable that in your application you want to be notified if some problem occurred during the operation.

At this point I haven’t found a reliable way for being notified about the result. Even using the startActivityForResult method of Ti.Android.currentActivity, instead of startActivity doesn’t help:

Ti.Android.currentActivity.startActivityForResult(intent, function(e) {

    if (e.resultCode == Ti.Android.RESULT_OK) {

      Ti.UI.createNotification({

        message: "Message Sent!"

      }).show();

    } else if (e.resultCode == Ti.Android.RESULT_CANCELED) {

      Ti.UI.createNotification({

        message: "Message sending cancelled"

      }).show();

    }

});

because the result value returned is always Ti.Android.RESULT_CANCELED. 

Here is a complete example showing two alternative techniques for sending text messages through intents on Android with Ti Mobile.

At this point, for Ti Mobile applications that need to be notified about the result of the operation, the only solution would be to create an Android module that wraps the android.telephony.SmsManager class. 

I’m currently experimenting  around some basic code for such a module, so stay tuned ;-)

Learning Javascript

Learning Javascript used to mean you weren’t a “serious software developer”. today, not learning it means the same thing.
@monkchips