Titanium.include() is one big leaky abstraction to drown in

NOTE: Titanium 1.5+ has come out and some of the gotchas listed below may have been fixed. So you should consult the Titanium documentation and community for updates!

The other day, I wrote about the little gotchas to getting started with Titanium. And don’t get me wrong, I think things like Titanium and Corona are great. They decrease developer time by getting the native code with all the calls and memory management right once, and then spending the rest of the time tying all the pieces together in Javascript and Lua respectively.

However, with Titanium (as of this writing, Titanium SDK 1.4.0), the abstraction is still leaky and you can waste hours when you stumble upon one of the leaky abstractions. You either have to not be afraid to dive down into the native code to see what it’s doing, or have the right mental model in your head.

The biggest leaky abstraction is Titanium.include(), or Ti.include() for short. It doesn’t behave at all like include or require in other languages, and it will fuck you up if you think it does. To save you time and sanity, I go over the gotchas of Ti.include() below that wasn’t at all obvious to me when I started. The short version of it if you don’t want to read all of this, is Titanium.include() is more like a cut and paste macro, than a true include or require. If you think of it that way, you’ll be fine.

What’s an execution context?

Execution context is probably the biggest thing you need to understand when doing Titanium apps. When you start the app, the main file is under the /Resources directory and it’s called app.js. The code in app.js is running in an execution context, much like a thread with a certain scope. When you create a child window in app.js, you get a child window that the Titanium guys call ‘lightweight’, 

var contactsWin = Ti.UI.createWindow({  
    title:'Contacts'
});

This lightweight window is running in the same execution context as app.js, meaning that you can access all variables created in the app.js file. However, if you create a window that uses a different file, aka, with the ‘url’ option, you create a child window that’s ‘heavyweight’:

var contactsWin = Ti.UI.createWindow({  
    url: "windows/contacts_index.js",
    title:'Contacts'
});

And the code for this new window in the file (contacts_index.js) will be run in a different execution context. That means none of the variables in the parent window will be available to you. Anything you want to use in that execution context will need to be included again. [1]

What is the scope for the files that are included?

Let’s say app.js includes utilities.js. The variables that you create in utilities.js are available in the app.js. So if utilities.js defines a variable named ‘foo’ at its top level, then it will also be available to all code in app.js following the line where utilities.js was included. 

What is the current directory for include?

With the two concepts above in mind, we’re ready for the first leaky abstraction of include. We ask: What is the current directory “./” for Ti.include()? If you write:

Ti.include('utilities.js');

Does it include utilities.js from the root path from which it is run, or does it include utilities.js from the directory that this file resides in?

The answer is neither. The current directory for include is the directory that the top level file of the current execution context. 

Let’s do an example. Suppose you have this directory structure where the includes in each file is:

  • app.js [ includes utilities.js ]
  • lib
    • utilities.js [ includes ajax.js ]
    • ajax
      • ajax.js
  •  windows
    • contacts_list.js [ includes utilities.js ]

app.js is the top level main file, and the windows/contacts_list.js is the code that’s run when app.js opens a new child heavyweight window. Thus the include statements each file should be:

 

  • app.js [ Ti.include(“./lib/utilities.js”); ]
  • lib
    • utilities.js [ Ti.include(“./lib/ajax/ajax.js”); ]
    • ajax
      • ajax.js
  • windows
    • contacts_list.js [ Ti.include(“../lib/utilities.js”); ]

Notice that app.js is in a different execution context then contacts_list.js, and therefore, contact_list include’s current directory for includes is in windows/ unlike app.js, which is in the root directory.

For the astute amongst you, you’ll notice that this example doesn’t actually work! The idea is correct, but the layout of the files in the directories is wrong. This is because utilities.js assumes that it’ll always be included by execution contexts with files at the root level. When app.js includes utilities.js, all is fine! But when contacts_list.js includes utilities.js, utilities.js turns around and tries to include “./lib/ajax/ajax.js”, which it won’t find, because it’s actually looking for “/windows/lib/ajax/ajax.js”. 

Thus, I structure my files so all the includes happen in the execution contexts in windows/, and I only use app.js to instanciate these execution contexts in windows/

 

  • app.js [ ]
  • lib
    • utilities.js [ Ti.include(“../lib/ajax/ajax.js”); ]
    • ajax
      • ajax.js
  • windows
    • contacts_list.js [ Ti.include(“../lib/utilities.js”); ]
  • models
    • contact.js [ Ti.include(“../lib/utilities.js”); ]

 

And any includes first go up a directory and then go down, so regardless of the execution context, the paths will always be correct.

If a file gets included twice, does the code actually get executed twice?

The answer is a disappointing yes. If you have a library or utility file that is included in different files, it will get executed twice. And no, there’s no preprocessor like in C to detect whether you’ve already included a file or not. However, you can detect whether a variable is defined or not and decide whether or not to execute the file contents. 

If you include utilities.js, it will probably have to do something like this:

if ( typeof(Utilities) === "undefined" ) {
  var Utilities = function() {
    //initialize stuff here
  };

  // "class" methods
  Utilities.foo = function(x) { return "foo " + x; };
  Utilities.bar = function(x) { return x + " bar"; };
  
  // "instance" methods   
  Utilities.prototype = {     baz: function(x) { return "baz " + x + " baz"; }   };
};

Whew, so should I use Titanium?

Finally, if you’re reading this to figure out whether you should use Titanium or not, I’d say, give it a shot. It does speed up your development time. For me, it sped it up about 3x. What use to take three weeks now took one. However, don’t expect everything to work in javascript world. If something breaks, be prepared to dive down into the Objective-C or Java depths below.[2] Really, I wish Titanium was more mature, as they’ve been at it for 2 years now, but I guess the more eyeballs there are, the better software gets.

Here’s a couple slides to get you started.

[1] You can attach variables as properties of a newly created child window to ‘pass them’ down to the child window.

var newContactWin = Ti.UI.createWindow({
  url: "contacts_new.js",
  title: "New Contact",
});
newContactWin.foo = "string to pass to child window";

[2] Or visit the community Q&A. Alternatively, pay Appcelerator to help you out.

Advertisements

7 thoughts on “Titanium.include() is one big leaky abstraction to drown in

  1. Titanium’s API is one of the most annoying frameworks I have ever worked with. Not to mention that their development environment GUI is incredibly buggy.Discovering how an API works from experimentation and trial and error instead of documentation should not be something that the guys at Appcelerator should be expecting from their developers.

  2. I've found that you don't actually need their Titanium tool for anything other than generating new projects. You're able to use Xcode to compile and debug Titanium's apps. I can't say that using Titanium's API has been a breeze, especially coming from Rails, where it's so well documented. Appcelerator would do well to focus more on writing better documentation, but that goes against their business model, which is selling tutorials and lectures on how to use their stuff. I personally don't like that business model as it tends to let the source and docs suffer. That said, I don't think it's beyond any developer to dive down into open source code. I'm of the opinion that you should be able to dive down into the library code if you need to, in order to figure out how something works, or fix it.

  3. this should be precursory reading before attempting any titanium development.. coming from a java, groovy, ruby etc way of thinking i have been battling the include quirks without fully getting what was going on. i now understand whats its doing i’ve been a lot more productive. it is still a less than ideal an approach they are using but at least i understand it now.. thanks 🙂

  4. Yup, no problem. The pain of the hours I spent shouldn’t be felt by other people. As long as it’s saved you time. The world can be more productive!I’ve moved on from titanium and decided to do mobile web instead. Much easier in some respects. Harder in that you have to deal with sys admin stuff!

  5. [1] You can attach variables as properties of a newly created child window to ‘pass them’ down to the child window.How does this work? I tried the code example and it seems like it works, but how do I access the value of ‘foo’ in ‘contacts_new.js’?

  6. I don’t remember exactly, as I did this a while back. You should ask the Titanium community and their docs to be sure. but I think you can just say ‘foo’ to access it.

  7. I would say one of the best cross platform mobile application development framework I have come across. Also with V8 being introduced the performance will be to watch for Android phones.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s