Saturday, May 21, 2011

Titanium MVC Pattern

Are you looking to simplify your Titanium programming model? Leveraging the MVC pattern will help produce cleaner code and promote reusability. Applying the MVC pattern within Titanium is relatively easy and it provides all the advantages we expect from MVC. Here is a quick example of the view and controller components:


View

The view is responsible for creating the window and adding the necessary UI components. Your views may optionally manage styling properties (color, font, and positioning). Ideally, the better practice is to extract all styling properties into external JSS files but I found this feature to be too quirky in its initial release.


/**
* View Template
* Usage: APP.ui.LoginView.createWindow();
*/

APP.ui.LoginView = (function(){

/* private methods to build UI specific components */
function buildWindow(){
return Ti.UI.createWindow({
title: 'Title',
barColor: '#225377',
backgroundColor: '#d4d2d3'
});
}

function buildUsernameTextField(win){
win.usernameTextField = Ti.UI.createTextField({
autocorrect: false,
hintText: 'Username',
left: 10,
width: 285,
suppressReturn:false,
autocapitalization: Ti.UI.TEXT_AUTOCAPITALIZATION_NONE,
clearButtonMode: Ti.UI.INPUT_BUTTONMODE_ONFOCUS,
borderStyle: Ti.UI.INPUT_BORDERSTYLE_NONE,
returnKeyType: Ti.UI.RETURNKEY_NEXT
});
}

/* Public API only exposes the createWindow method. */
return {
createWindow: function(){
var win = buildWindow();
buildUsernameTextField(win);
//buildComponentX(win);
return win;
}
};
})();


Controller

The controller is responsible for managing events (button taps) and navigation. Events handlers communicate with server-side services when necessary.

/**
* Controller Template
* Usage: APP.ui.LoginController.init();
*/

APP.ui.LoginController = (function(){

/* Private helper methods */
function setEventListeners(win){
win.usernameTextField.addEventListener('return', function(){
win.passwordTextField.focus();
});

win.passwordTextField.addEventListener('return', function(){
handleLoginEvent(win);
});
}

/* Public API only exposes init method. */
return {
init: function(){
var win = APP.ui.LoginView.createWindow();
setEventListeners(win);
win.open();
}
};
})();


Model

The model is typically a JSON payload retrieved from a Restful service. For example, the handleLoginEvent will return user profile information after a successful login. We can save this object locally and leverage it on subsequent screens.


Advantages:
  • Small objects and methods.
  • Improved organization and readability
  • Promotes reusability of views. Also, the decoupled views allow you to swap in native-specific UIs when necessary all while reusing a single controller.
  • Simpler maintenance.
  • Allows for simpler unit testing with responsibilities split into separate objects.


I expect many developers may fall in the trap of modeling their JavaScript according to the KitchenSink examples. While those examples are useful, their programming practices should not be followed for a production ready app. For example, their examples do not leverage closure to eliminate global scope, they do not use a namespace convention for improved organization, and all MVC responsibilities were bundled within a single object.