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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
Since the method is called synchronously, also the HTTP request must be performed in a synchrounous way:
[request startSynchronous];
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 modifiedApplicationRouting.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:
1 2 3 4 5 6 7 8 9 |
|
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!