Rigel Group

They shoot Yaks, don't they?

Building iPhone Apps Using Titanium and CoffeeScript

We have been using Titanium to build mobile apps for some time now. It is truly amazing how much more productive we are as opposed to when we use POOC (Plain Old Obj-C). Granted, Titanium is not a great fit for some applications, but for your standard network and data-heavy line of business applications, it rocks.

But, Titanium does not give you a lot of guidance on how you should write your app. They provide all the pieces you need, but folks are still figuring out the best way to structure things. So, we thought we would show you our particular technique for putting together a Titanium mobile app.

The first thing we did, was ditch Javascript. To be honest, something about it just makes our eyes bleed. Fortunately, jashkenas has come to our rescue with a Ruby-like language that compiles down to Javascript, called CoffeeScript. CoffeeScript makes Javascript a joy to use, and because it “compiles” to plain ole’ Javascript, you can use it anywhere, including Titanium. (Basically, you run the command coffee -w -c *.coffee in whatever directory your files are in, and it continually watches for any changes and immediately compiles the *.coffee into a *.js file.) So to be clear, you are only ever referencing the .js files in your Titanium project, Titanium doesn’t know anything about CoffeeScript or how to parse/use it.

One interesting thing CoffeeScript does, is compile each CoffeeScript file into its own namespace inside an anonymous Javascript function. This is great, because it isolates each file and prevents you from polluting the global namespace. For example, this CoffeeScript code:

1
2
show_flag = true
alert "Hello World!" if show_flag

gets translated into this Javascript code:

1
2
3
4
5
6
7
(function() {
  var show_flag;
  show_flag = true;
  if (show_flag) {
    alert("Hello World!");
  }
})();

Another thing CoffeeScript enables, is an easier-on-the-eyes way to creates “classes”, for example (from the CoffeeScript docs):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Animal
  constructor: (@name) ->

  move: (meters) ->
    alert @name + " moved " + meters + "m."

class Snake extends Animal
  move: ->
    alert "Slithering..."
    super 5

class Horse extends Animal
  move: ->
    alert "Galloping..."
    super 45

sam = new Snake "Sammy the Python"
tom = new Horse "Tommy the Palomino"

sam.move()
tom.move()

(If you really want to see what that gets translated to, put on some shades and click here)

So, when using CoffeeScript in Titanium apps, we can use these two features to help us structure things in a way that keeps each window isolated in its own namespace, yet allows us easy access to global objects as well. To start, we set up our app.js file, which Titanium runs on app startup. We keep this file as a plain Javascript file, and define a variable called root, to which we will attach anything that we want access to globally. We also include each window’s CoffeeScript file (well, technically we include the Javascript file that the CoffeeScript file was translated into). Finally, we include a main file, which sets up the main tab group and kicks everything off.

app.js
1
2
3
var root = {};
Ti.include('js/generic_window.js');
Ti.include('js/main.js');

In the generic_window.coffee file, we create a class definition for a generic window, and actually create a Titanium window in the constructor. We then attach the class definition to the root object so we can get at it from anywhere.

generic_window.coffee
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class GenericWindow
  constructor: (theTitle, theText) ->
    @win = Ti.UI.createWindow({title:theTitle,backgroundColor:'#fff'})
    label = Titanium.UI.createLabel({
      color: '#999',
      text: theText,
      font: {
        fontSize: 20,
        fontFamily: 'Helvetica Neue'
      },
      textAlign: 'center',
      width: 'auto'
    })
    @win.add(label);

root.GenericWindow = GenericWindow

Now the main file sets up the tabgroup, and actually instantiates some windows from the above generic window class…

main.coffee
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
tabGroup = Titanium.UI.createTabGroup({ barColor:'#336699'})
Titanium.UI.setBackgroundColor('#000')

# Attach the window instance to root so we can get to it from anywhere
root.Win1 = new root.GenericWindow('Win1','I am Window 1')

tab1 = Titanium.UI.createTab({
  icon:'KS_nav_views.png',
  title:'Win1',
  window: root.Win1.win
})
tabGroup.addTab(tab1)

# Attach the window instance to root so we can get to it from anywhere
root.Win2 = new root.GenericWindow('Win2','I am Window 2')

tab2 = Titanium.UI.createTab({
  icon:'KS_nav_views.png',
  title:'Win2',
  window: root.Win2.win
})
tabGroup.addTab(tab2)

tabGroup.open({transition:Titanium.UI.iPhone.AnimationStyle.FLIP_FROM_LEFT})

Now, let’s add a custom property to our generic window class, so add this line to the constructor of generic_window.coffee:

1
@custom1 = "Default Value"

This creates a property and gives it a default value. To access it, you could say root.Win1.custom1. Now let’s add an event handler that displays an alert box containing the value of @custom1:

generic_window.coffee
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class GenericWindow
  constructor: (theTitle, theText) ->
    # Save off the context of this object to a local var 'self'
    self = this
    @custom1 = "Default Value"
    @win = Ti.UI.createWindow({title:theTitle,backgroundColor:'#fff'})
    label = Titanium.UI.createLabel({
      color: '#999',
      text: theText,
      font: {
        fontSize: 20,
        fontFamily: 'Helvetica Neue'
      },
      textAlign: 'center',
      width: 'auto'
    })
    @win.add(label);

    @win.addEventListener('click', () ->
      alert(self.custom1)
    )

root.GenericWindow = GenericWindow

If you would like to see how it all hangs together, clone the GitHub repo here and try it out. The bottom line is that before CoffeeScript, our Titanium apps tended to resemble spaghetti, but now we are able to encapsulate functionality and just generally make things look much cleaner. Sure, you don’t need CoffeeScript to do this, but it sure makes it a lot more fun!