Titanium best practices, Patterns 

& TIPS


Boydlee Pollentine

Accenture / Bangalore, India

WHO AM I?


Author - Appcelerator Titanium Smartphone Cookbook
Co-Author - Titanium Patterns & Practices

Original Titan & TCAD 
Organiser of tiConf EU, tiConf USA & tiConf Australia.

Blog - http://boydlee.com
Twitter - @boydleep

Let's start at the very basic level - Javascript.

THE RISE & RISE OF JAVASCRIPT


Four major factors:

(1) Better browsers, faster JS Engines (WebKit,  V8)

(2) jQuery (and to a lesser extent, MooTools, YUI etc)

(3) JavaScript on the server - moving from sychronous to asynchronous calls (e.g. Node.JS)

(4) Mobile - Cross compilation frameworks (i.e. Titanium), Web Based frameworks (i.e. Phonegap)


NOT ALL OPERATORS ARE CREATED EQUAL...


JavaScript typecasts variables when they are 
compared to one another. That's why:

false == 0
and
false == 0.0
and
false == "0"

USE StrICT COMPARISON OPERATORS INSTEAD


Instead of == use ===

Instead of != use !==


Whilst performance effects may be negligible using 
strict comparisons makes your code less likely to 
break and easier to understand.

Avoid GLOBALS



myVariable = false;  //BAD

Always use the var keyword when declaring variables.

var myVariable = false; //GOOD


Using Prototype will help reduce or remove Globals and keep your code neat and efficient. More on that later...

invoking constructors


There is no need to invoke constructors directly.

//Don't use this method of invokingvar obj = new Object();     obj.name = 'Boydlee';     obj.genderCode = 1;
//Instead use the more common and easier to read object literal methodvar obj = { name: 'Boydlee', genderCode: 1};

The same principle applies to creating Arrays.


 //Don't use this method var myArray = new Array(); myArray[0] = 'Item number 1'; myArray.push('Item number 2');
//Instead use: var myArray = ['Item number 1', 'Item number 2'];

JavaScript is case sensitive


When comparing strings (unless case sensitivity is really required), then remember to use the toLowerCase() or toUpperCase() built-in functions, e.g.

        var companyName = "Accenture";
       "accenture" === companyName.toLowerCase(); //true 

        switch (companyName.toLowerCase()) {
              case "accenture":
                    //do some work!
               break;
        }


you can access key values in objects


                    var person = {
                              name: 'Boydlee',
                              genderCode: 1,
                              height: '6ft 0'
                     };

                    for(key in person) {
                             console.log(key + ' = ' + person[key]);
                    }

WHAT IS THIS?


this refers to the current scope...

It can refer to the current object or function, or
it can refer to the global scope, but it all depends on where you access it.
 
                           this.context = 'Global';
                                
                           function myFunction() {
                                 alert(this.context); //undefined
                          }

THIS - CALLBACK HELL


Consider the following code:

function getData() {

this.id = 1; console.log(this.id); //prints '1' makeHttpRequest("http://myserver.com?id=" + this.id, callback(e) { console.log("Data received for id: " + this.id); //prints "Data received for id: undefined" //in this example "this" refers to the context of "callback()" //not the context of "getData()" }); } 


UNDERSTANDING the use of the THIS keyword is imperative when implementing commonjs modules.


But more on that later...

EVERY TIME YOU DON'T USE CURLY BRACKETS & SEMi-COLONS

A KITTEN GETS SAD



A few other tips...


Put your curly bracket on the first line of your statement
 
                                     function me() {
                                           return 'Hello you';
                                     }


Declare multiple variables on the same line

var firstName, lastName, emailAddress;

A FEW OTHER TIPS...


Follow the Titanium pattern of using camelCase:

function goWest() {}
var myVariableName = 'Test';
var win = Ti.UI.createWindow();


Use shortcuts for simple if statements:

var isEnglish = (beerTemp === 'warm') ? true : false;

MOST IMPORTANTLY



Write your code neatly and formatted it nicely. 
Avoid excessive nesting of statements.

Tab indentation or spaces? Doesn't matter so much.

Remember you should endeavour to write code that is easily readable by HUMANS first and foremost
and MACHINES second.

A BRIEF HISTORY OF TITANIUM DEVELOPMENT...


In the beginning, the  flying spaghetti code monster decreed there were no rules.

THEN, to add some structure and save memory, we all started using namespaces...


                         var myApp = myApp || {};

                         myApp.myFunction =  function() { 

                                     return "Hello!" ;

                            }



This saved us from polluting the global scope and helped ensure that apps didn't suffer from 

"memory starvation"


But...

IT wasn't quite perfect.

INSTEAD, APPCELERATOR IMPLEMENTED COMMONJS.


CommonJS is a specification that defines how JavaScript should be treated outside of the browser. 

Specifically, initial versions of CommonJS are primarily concerned with how JavaScript modules are 
defined and "required". 

A CommonJS Module...


            var myApp =  function() { 

                     this.message = "Hello there ";

              }


             myApp.prototype.sayHello = function(_name) {

                     return this.message + _name;

             }


            exports.myApp = myApp;

NOW WE HAvE MORE GRANULAR CONTROL OVER HOW OUR CODE IS DEFINED and RE-USED.


This meant we could write not just better code - but code that was more efficient and didn't suffer from the memory issues of other mechanisms.

Unfortunately, it seems many people still make some very simple mistakes...

Memory Management IN TITANIUM


The two most common causes of performance and memory issues in Titanium:

                        1) Not removing event listeners!

                       2) Not closing or removing objects 

EVENT LISTENER NO-NO's


        myButton.addEventListener('click', function(e) {
             //create a view object
             var view = Ti.UI.createView({
                title: 'My Great Interface',
                width: Ti.UI.FILL,
                height: Ti.UI.FILL
            });
        
            view.addEventListener('click', function(e2){ });
      });

HTTP REQUESTS



responseText !== responseData

Use the correct response type when accessing returned data from a HTTP request. 

TABLEviewS


Always use the "className" property when creating TableRows - this can speed up rendering of tables between 50-75%.

Not all TableView features are cross compatible, e.g. PullToRefresh only works on iOS (Android tables have no concept of a "HeaderView").

The less you overload your tables with multiple images and nested views, the better.

WINDOWS


Close windows whenever they are not in use,

Don't create windows through orphaned processes where they can't be closed properly,

Don't load windows through URL properties any more!
This generates a new JavaScript instance which could mean memory leaks and performance issues.

e.g. Ti.UI.createWindow({ url: '/mywin.js' }) //bad!



COMMONJS


One of the main advantages of CommonJS is the way it lends itself to creating well- structured, separated code. 

It facilitates the creation of common modules enabling a constructive codebase that is easy to understand and maintain. 

A good example of a common module would be one that contains the geolocation code and is then used across the whole application.

Use it!


ONLY EXPOrt WHAT IS REQUIRED


Making functions and variables private to a module maintains the module's integrity. Do not export all the functions in a module unless they are actually called from the requiring module. 

Export the required functions and variables at the end of the module, not by default. 

EXPORTING METHODS



Don't use*:

module.exports = myFunction;

Instead use:

exports.myFunction = myFunction;


*unless you only have a single object to export

Use prototype


Prototype allows you to extend an object by adding properties or methods to it.

                      var myModule = function(){
                           this.version = '1.0b';
                      }

                      myModule.prototype.getVersion(){
                           return this.version;
                      }
 
                      exports.myModule = myModule;

OTHER TIPS?


Be careful about the usage of WebViews and Maps in particular - both of these are exceedingly memory hungry.

Be careful about using reserved keywords such as id, hash, prototype, export and this.

Don't break your apps or cause future collisions by trying to extend the Titanium namespace.

i.e. Ti.Platform.myId = function(){}    //bad 

Other tips?


Plan your app strategy carefully - it can be very hard to tack on "Android" compatibility later on, especially if you utilise a lot of iOS only methods.

Remember the simulator is much quicker than a real device so test early and often.

If you need to store persistent data, Ti.App Properties is fine. If it needs to be secure, use the keychain.


iOS TIPS


The simulator on iOS is great, so use it.

Remember you can always build and package and test apps using XCode if needed.

Use Instruments to ensure your application is performing well and not leaking memory.

Upgrading to new XCode versions can break old projects. You can always install older Simulators and SDK's through the XCode Manager.

Android


Buy a decent device, like a Samsung Galaxy or high-end HTC phone, because the Android Emulator SUCKS.

The Android Emulator sucks.

Did I mention the Android Emulator sucks?

If you do need to use the Emulator, install HAXM and use the x86 version as it's considerably better. Be aware though it only uses the 3.x SDK and can't run Maps.

Android TIPS


Always use "DP" values instead of "PX"
dp values are density independent and will ensure your layout across devices (particularly Android) will look correct. This is now the default setting in tiapp.xml.

Forget low-end devices unless you really, really need to target them. That also goes for anything pre-Android 3.0 SDK in my opinion.

Android's WebView engine is rubbish - the new IE6
Use it sparingly and not at all if you can get away with it.



OTHER TIPS?


There's lots of great JavaScript libraries out there you can use in your Ti projects - moment.js and underscore.js to name just two. These are built-in to Alloy.

You can generally convert any JS library that doesn't interact with the DOM to a CommonJS module.

The Marketplace is always a good place to shop for a solution before you try to go and re-build the wheel.

OTHER TIPS?


If you're running out of HDD space, you can probably blame the iOS emulator. Regularly delete all the files in your ~/Application Support/IOS Simulator/tmp directory.

Don't just update SDK's when you're in the middle of a project - past experience tells me there's always some kind of breaking change.

Turn off Ti.Analytics unless you're actually paying for it and using it. Other free options exist (Flurry, etc).

Alloy


Alloy is the framework recommended by Appcelerator for Titanium Development.

Based loosely on the MVC pattern.

Alloy allows for a greater structural breakdown of your design, model logic and controllers.

Uses "TSS" for styling screens and defining layout properties within your app.

Titanium & AppCelerator STUDIO


The debugging tools (particularly on iOS) are pretty good. Use the expressions pane in Studio to inspect current variables and execute code.

Remember Alloy still needs a "debugger;" line wherever you want to hit a breakpoint in code.

It can be very memory hungry so consider shutting it down every so often.

getting community help


There is a group of us who watch every question posted on the Q&A forums, so to get your question answered remember:

  • Provide a full summary of the problem and your environment (SDK versions, iOS Version, etc).
  • Always provide a code sample - a GIST is even better.



Q&A


Javascript and Titanium Best Practices

By boydlee

Javascript and Titanium Best Practices

  • 4,190