For many, building a Titanium app will be their first experience with a large, complete JavaScript project. Whether you are designing a small expense tracking system or a complete CRM tool, implementing proper design patterns in Titanium will improve the performance and maintainability of your app.
The cross-platform nature and underlying architecture of Titanium influences how many common design patterns can be implemented. In this chapter, we will demonstrate how to apply patterns to increase speed of development while implementing best practices for multiple device support.
Appcelerator Titanium Mobile is a platform for building cross-platform native mobile applications using modern web technologies, such as JavaScript, CSS, and HTML. Titanium Mobile is an open source project developed by Appcelerator Inc and licensed under the OSI-approved Apache Public License (Version 2).
The Titanium Mobile project is one of the most active on Github with a large number of commits each day. The Github repository is the focal point for many in the community including module developers, app developers needing night builds, and individual contributors to the Titanium project.
The Titanium ecosystem is one of the largest in the industry with a community of more than 450,000 worldwide developers running apps on 10 percent of the world's devices. Appcelerator boasts one of the largest mobile marketplaces providing third-party components for Titanium Mobile platform.
Titanium is a module-based mobile development platform consisting of JavaScript and native platform code (Java, Objective-C, and C++). The architectural goal of Titanium is to provide a cross-platform JavaScript runtime and API for mobile development; this differs from other frameworks' approaches of building "native-wrapped" web applications.
Titanium uses a JavaScript interpreter to create a bridge between your app's JavaScript code and the underlying native platform. This approach allows Titanium to expose a large number of APIs, and native UI widgets without sacrificing performance. Titanium's UI controls are truly native and not visually emulated through CSS. Thus, when you create a Ti.UI.Switch
, it is actually using the native UISwitch control on iOS.
Each Titanium application is organized into layered architecture consisting of the following major components:
Your JavaScript code: At compile time, this will be encoded and inserted into Java or Objective-C files
Titanium's JavaScript interpreter: On Android V8 or JavaScriptCore for iOS
The Titanium API: This is specific for a targeted platform created in Java, Objective-C, or C++
Native Custom Modules: A large variety of open source and commercial modules are available
At runtime, the Titanium SDK embedded within your app creates a native code JavaScript execution context. This execution content is then used to evaluate your app's JavaScript code. As your JavaScript is executed, it will create proxy objects to access native APIs such as buttons and views. These proxies are special objects that exist both in the JavaScript and native contexts acting as a bridge between the two environments.
For example, if we have a Ti.UI.View
object and we update the backgroundColor
to blue
, the property is changed in JavaScript and then the proxy then updates the correct property in the underlying native layer as shown in the following diagram:

Titanium provides a high-level cross-platform API, however it is not a write once, run anywhere framework. When building cross-platform apps, it is recommended to adopt a "write once, adapt everywhere" philosophy. With Titanium you can add platform-specific code to handle each platform's different UI requirements, while keeping your business logic 100 percent cross-platform compatible.
Building best of breed cross-platform applications, Titanium provides tools to:
Identify the platform and model at runtime
Ability to handle platform-specific resources at build time
Apply platform and device-specific styling
In addition to platform tooling, the Titanium API is designed to assist with cross-platform development. Each major component such as Maps
, Contacts
, and FileSystem
are separated into its own component namespaces under the top-level namespace called Ti
or Titanium
. These component namespaces then have their own child namespaces to segment platform-specific behavior.
An example of this segmentation is the Ti.UI namespace
, which contains all UI components. This namespace contains common APIs such as Ti.UI.View
and Ti.UI.TableView
. Additionally, the Ti.UI
namespace has platform-specific child namespaces such as Ti.UI.iPad
containing controls such as Ti.UI.iPad.Popover
. The same design applies to non-visual APIs such as Ti.Android
, a namespace which contains Android-specific behavior.
Using namespaces is important in Titanium app development, as it helps organize your code while not polluting the global namespace. The practice of creating variables and methods without being associated with a namespace or other scoping condition is called polluting the global namespace . Since these functions and objects are scoped globally, they will not be eligible for collection until the global namespace loses scope during application shutdown. This can often result in memory leaks or other unwanted side effects.
Organizing your application code using CommonJS modules is a best practice in Titanium development. CommonJS is a popular specification for creating reusable JavaScript modules and has been adopted by several major platforms and frameworks such as Node.js and MongoDb.
CommonJS modules help solve JavaScript scope problems, placing each module within its own namespace and execution context. Variables and functions are locally scoped to the module, unless explicitly exported for use by other modules.
In addition to assisting with JavaScript scope concerns, CommonJS provides a pattern to expose a public stable interface to program against. The information-hiding design pattern allows module developers to update the internals of the module without breaking the public contract or interface. The ability to maintain a stable public interface in JavaScript is the key part of writing maintainable code that will be shared across apps and teams.
Titanium has implemented CommonJS in a similar fashion to Node.js in that you use the require
method to return a JavaScript object, with properties, functions, and other data assigned to it, which form the public interface to the module.
The following screenshots illustrate the example app used to demonstrate the CommonJS high-level concepts that will be used throughout the book.

Adding the CommonJS modules used in this recipe is straightforward and consists of copying the datahelper.js
and dateWin.js
files into the root of our Titanium project as shown in the following screenshot:

The following recipe illustrates how to use CommonJS to create both UI
and Tools
modules. In the following example, a simple app is created, which allows the user to increase or decrease the date by a day.
In our app.js
we create our application namespace. These namespace variables will be used to reference our CommonJS modules later in the example.
//Create our application namespace var my = { ui:{ mod : require('dateWin') }, tools:{}, controllers:{} };
Tip
Downloading the example code
You can download the example code files for all Packt books you have purchased from your account at http://www.PacktPub.com. If you purchased this book elsewhere, you can visit http://www.PacktPub.com/support and register to have the files e-mailed directly to you.
Ti.UI.Window
is then created using the my.ui.mod
already added to our app namespace. The open
method is then called on our win
object to open our example app's main window.
my.ui.win = my.ui.mod.createWindow(); my.ui.win.open();
In the Resources
folder of our project, we have a CommonJS module datehelpers.js
. This module has the following code:
The
helpers
method is created within thedatahelpers
module. This function is private by default until it is exposed using theexports
object.var helpers = function(){ var createAt = new Date();
The
createdOn
method is added to thehelpers
function. This function returns thecreateAt
variable. This function is used to provide a timestamp value to demonstrate how the module can be initialized several times. Each time a new session is created for the module, thecreateAt
variable will display the newly initialized timestamp.this.createdOn = function(){ return createAt; };
The
addDays
method is added to thehelpers
function. This method increases the provided date value by the number of days provided in then
argument.this.addDays = function(value,n){ var tempValue = new Date(value.getTime()); tempValue.setDate(tempValue.getDate()+n); return tempValue; } };
The module.exports
is the object returned as the result of a require
call. By assigning the helpers
function to module.exports
, we are able to make the private helpers
function publically accessible.
module.exports = helpers;
Also included in the Resources
folder of our project is a CommonJS module dateWin.js
. The following code section discusses the contents of this module.
Use the
require
keyword to import thedatehelpers
CommonJS module. This is imported in themod
module level variable for later usage.var mod = require('datehelpers');
The
createWindow
function returnsTi.UI.Window
allowing the user to work with the recipe.exports.fetchWindow=function(){
Next a new instance of the
dateHelper
module is created.var dateHelper = new mod();
The next step in building the
createWindow
function is to set thecurrentDateTime
module property to a default of the current date/time.var currentDateTime = new Date();
The
Ti.UI.Window
object, which will be returned by thecreateWindow
function, is then created. This will be used to attach all of our UI elements.var win = Ti.UI.createWindow({ backgroundColor:'#fff' });
The
dateDisplayLabel
is used to show the result of thedatehelper
module as the user increases or decreases the date value.var dateDisplayLabel = Ti.UI.createLabel({ text:String.formatDate (exports.currentDateTime,"medium"), top:120, height:50, width:Ti.UI.FILL, textAlign:'center', color:'#000', font:{fontSize:42} }); win.add(dateDisplayLabel);
The
addButton
is used later in this recipe to call thedatehelper
module and add days to the current module value.var addButton = Ti.UI.createButton({ title:"Add Day", top:220, left:5, width:150, height:50 }); win.add(addButton);
The
subtractButton
is used later in this recipe to call thedatehelper
module and reduce the date of the current module value.var subtractButton = Ti.UI.createButton({ title:"Subtract Day", top:220, right:5, width:150, height:50 }); win.add(subtractButton);
The following code in the
addButton
andsubtractButton
click handlers shows how theAddDays
method is called to increment thecurrentDateTime
property of the module.addButton.addEventListener('click',function(e){
The following line demonstrates how to increase the
currentDateTime
value by a single day:exports.currentDateTime = dateHelper.addDays(currentDateTime,1);
Update the
dateDisplayLabel
with the new module value.dateDisplayLabel.text = String.formatDate( exports.currentDateTime,"medium"); }); subtractButton.addEventListener('click',function(e){
The following code snippet demonstrates how to reduce the
currentDateTime
by a day:exports.currentDateTime = _dateHelper.addDays(currentDateTime,-1);
Update the
dateDisplayLabel
with the new module value.dateDisplayLabel.text = String.formatDate( currentDateTime,"medium"); }); return win; };
Creating a module is easy. You simply add a JavaScript file to your project and write your application code. By default, any variables, objects, or functions are private unless you have added them to the module
or exports
objects. The module
and exports
objects are special JavaScript objects created by Titanium and returned by the require
method.
To use a CommonJS module you must use the globally available require
function. This function has a single parameter through which you provide the path to your JavaScript module. The following line demonstrates how to load a CommonJS module called datehelpers.js
located in the root of your Titanium project.
var myModule = require('datehelpers');
Note
When providing the require
function with an absolute path, Titanium will start from the Resources
folder of your project.
Titanium has optimized the module loading process so that when a module is first loaded using the require
method, it is then cached to avoid the need to re-evaluate the module's JavaScript. This approach significantly improves the load performance for modules which share common dependencies. It is helpful to keep in mind the JavaScript is not re-evaluated if you have the need to manage/alter the module on load.
Adding a property to your CommonJS module is easy. You simply attach a variable to the exports
object.
The following snippet demonstrates how to create a basic CommonJS property with a default value.
exports.myProperty = "I am a property";
More complex object properties can also be created, for example, as follows:
exports.myObjectProperty = { foo:1, bar:'hello' };
You can also use a private variable to set the initial property's value. This often makes for more readable and streamlined code.
Create a local variable reflecting your business need.
var _myObjectProperty = { foo:1, bar:'hello' };
You can then assign the local variable to a property attached to the exports
object, for example, as follows:
exports.myObjectProperty = _myObjectProperty
Creating public functions is easy in CommonJS, you simply add a function to the exports
object. Create an Adddays
method as part of the exports
object. This function accepts a date in the value
parameter and an integer as the n
value.
exports.AddDays = function(value,n){
Create a new variable to avoid the provided value from mutating.
var workingValue = new Date(value.getTime());
Increase the date by using the n
value provided. This could be either a positive or negative value. Negative values will decrease the date value provided.
workingValue.setDate(workingValue.getDate()+n);
Return the new adjusted value.
return workingValue; };
You can also assign an exports
method to a privately scoped function. This can be helpful in managing large or complex functions.
Create a locally scoped function named addDays
.
function addDays(value,n){ var workingValue = new Date(value.getTime()); workingValue.setDate(workingValue.getDate()+n); return workingValue; };
The addDays
function is then assigned to exports.AddDays
exposing it to callers outside of the module.
exports.AddDays = addDays;
Titanium provides the ability to create a module instance object using module.exports
. This allows you to create a new instance of the function or object attached to module.exports
. This is helpful in describing one particular object and the instance methods represent actions that this particular object can take.
This pattern encourages developers to think more modularly and to follow the single responsibility principle as only one object or function can be assigned to the module.exports
.
The following code snippets demonstrate how to create and call a module using this pattern:
Using Titanium Studio, create the employee (
employee.js
) module file.Next create the
employee
function.var employee = function(name,employeeId,title){ this.name = name; this.employeeId = employeeId; this.title = title; this.isVIP = function(level){ return (title.toUpperCase()==='CEO'); } };
Then assign the
employee
function tomodule.exports
. This will make theemployee
function publicly available to call usingrequire
.module.exports = employee;
Using
require
, a reference to the module is created and assigned to theemployee
variable.var employee = require('employee');
Next the
bob
andchris
objects are created using new instances of theemployee
object created earlier.var bob = new employee('Bob Smith',1234,'manager'); var chris = new employee('Chris Jones',001,'CEO');
Finally, the properties and functions on the
bob
andchris
objects are called to demonstrate each object's instance information.Ti.API.info('Is ' + bob.name + ' a VIP? ' + bob.isVIP()); Ti.API.info('Is ' + chris.name + ' a VIP? ' + chris.isVIP());
The CommonJS implementation across Android and iOS is largely the same with one major scoping exception. If you are using a version of the Titanium framework below version 3.1, Android scopes all variable access to the module itself, while iOS allows the module to access objects outside of the module already loaded in the execution context. This should be considered an anti-pattern as it breaks many of the encapsulation principles the CommonJS specification was designed to prevent.
In Titanium Version 3.1, the decision has been made to deprecate global scope access in both iOS and Android in favor of the new Alloy.Globals
object. You can read more about the Alloy.Globals
object at http://docs.appcelerator.com/titanium/latest/#!/api/Alloy.
The following recipe demonstrates this common anti-pattern and highlights the CommonJS implementation differences in this area between iOS and Android.
//Create our application namespace var my = { tools: require('scope_test'), session:{ foo: "Session value in context" } };
The testScope
method is called on the tools
module. This demonstrates how CommonJS module scope anti-pattern works.
my.tools.testScope();
The tools
module containing the testScope
method is part of this recipe's code and can be found in the scope.js
file root of our project. This module contains the following code:
exports.testScope = function(){ Ti.API.log ("Test Module Scope - Foo = " + my.session.foo); return my.session.foo; };
The scope-access anti-pattern is shown when calling the my.tools.testScope()
method. In iOS, my.tools.testScope()
returns "Session Value in context"
, because it has access to my.session.foo
from the current execution context. In Android, prior to Titanium SDK Version 3.1, undefined object used to be returned as the module did not have access to the my.session.foo
object. In the Titanium SDK Version 3.1 and greater, Android now returns "Session Value in context"
as it has access to the my.session.foo
object.
Access to global scope has been deprecated on both platforms starting with Titanium SDK Version 3.1 and will be removed in a future version of the SDK. If you have previously implemented this anti-pattern, corrective action is recommended as the deprecation of this feature will cause breaking changes within your app.
To learn more about the CommonJS specification, please visit the wiki on CommonJS.org at http://wiki.commonjs.org/wiki/CommonJS
For more details and examples on Titanium's implementation of the CommonJS specification, please review their guides at http://docs.appcelerator.com/titanium/latest/#!/guide/CommonJS_Modules_in_Titanium
Handling different devices, platforms, and models is often the biggest challenge with cross-platform development. Titanium provides the Ti.Platform
namespace to help you make decisions in your code on how to handle runtime execution.
In the following recipes, we will walk through how to create a PlatformHelpers
CommonJS module containing convenience methods to solve many of your platform-related queries. The following screenshots demonstrate this recipe while running on both the iPhone and Android platforms.

The Ti.Platform
namespace is helpful for providing platform and device-specific details, but often you need a high level of detail such as when you have a tablet running your app and if so if it is an iPad Mini.
The following app.js
demonstrates the PlatformHelpers
module described earlier in this chapter. This sample app presents a window with your device details. Give it a try on all of your Android and iOS devices.
To create our same app, first we declare our app namespace and import our
PlatformHelper
module.//Create our application namespace var my = { platformHelpers : require('platform_helpers') }; (function(){
Next we create the window in which we will present the device information.
var win = Ti.UI.createWindow({ backgroundColor:'#fff' });
Next we create an empty array to store our device details.
var deviceData = [];
An entry is added to
deviceData
if your device is running on a simulator.The
isSimulator
property is used to show the simulator message, only if the recipe is currently running on a platform simulator or emulator.if(my.platformHelpers.isSimulator){ deviceData.push({ title:'Running on Simulator', child:false }); }
Next the platform, operating system's name, and manufacturer are added to
deviceData
.deviceData.push({ title:'Platform: ' + (my.platformHelpers.isAndroid ? 'Android' : 'iOS'), child:false }); deviceData.push({ title:'osname: ' + my.platformHelpers.osname, child:false }); deviceData.push({ title:'manufacturer: ' + my.platformHelpers.manufacturer, child:false });
The following statement adds a flag to
deviceData
indicating if the current device is a tablet form factor:deviceData.push({ title:(my.platformHelpers.isTablet ? 'Is a Tablet' : 'Is not a Tablet'), child:false });
Next we add the device model and specify if it supports background tasks in the
deviceData
array.deviceData.push({ title:'Model: ' + my.platformHelpers.deviceModel, child:false }); deviceData.push({ title:'Backgrounding Support: ' + my.platformHelpers.supportsBackground, child:false });
Screen dimensions are the most commonly used properties. The following snippet adds the height and width of the screen to the
deviceData
array:deviceData.push({ title:'Height: ' + my.platformHelpers.deviceHeight + ' Width: ' + my.platformHelpers.deviceWidth, child:false });
If your app is currently running on an iOS device, the following snippet adds the device type and specifies if it is retina enabled, to the
deviceData
array.if(my.platformHelpers.isIOS){ deviceData.push({ title:'iOS Device Type: ' + (my.platformHelpers.iPad ? (my.platformHelpers.iPadMiniNonRetina ? 'iPad Mini' : 'Is an iPad') : 'iPhone'), child:false }); deviceData.push({ title:'Is Retina : ' + my.platformHelpers.isRetina, child:false }); }
The
PlatformHelper
module provides a great deal of data. To best display this, we will be using aTi.UI.TableView
with thedeviceData
array that we have built in an earlier code snippet.var tableView = Ti.UI.createTableView({top:0, data:deviceData}); win.add(tableView); win.open(); })();
The device platform lookups will frequently be accessed across your app. To avoid performance issues by repeatedly crossing the JavaScript Bridge, we import the values we will use and assign them to properties in our CommonJS PlatformHelpers
module.
exports.osname = Ti.Platform.osname; exports.manufacturer = Ti.Platform.manufacturer; exports.deviceModel = Ti.Platform.model; exports.deviceHeight = Ti.Platform.displayCaps.platformHeight; exports.deviceWidth = Ti.Platform.displayCaps.platformWidth; exports.densitylevel = Ti.Platform.displayCaps.density; exports.deviceDPI = Ti.Platform.displayCaps.dpi;
It is often helpful to have a Boolean indicator for the platform with which you are working. The following snippet shows how to create isAndroid
and isIOS
properties to accomplish this:
exports.isAndroid = exports.osname === 'android'; exports.isIOS = (exports.osname === 'ipad' || exports.osname === 'iphone');
Depending on your platform, several features may not work in that platform's simulator or emulator. By using the isSimulator
property, detect which environment you are in and branch your code accordingly.
exports.isSimulator = (function(){ return (Ti.Platform.model.toUpperCase() === 'GOOGLE_SDK' || Ti.Platform.model.toUpperCase() === 'SIMULATOR' || Ti.Platform.model.toUpperCase() === 'X86_64') })();
Mobile apps are often required to perform tasks in the background. This recipe demonstrates how to perform a version-based capability check to determine if the device your application is running on supports backgrounding/multitasking.
exports.supportsBackground = (function(){
Now perform the following steps:
First check if the user is running on Android. If so, return
true
as most Android ROMs support background processing by default.if(exports.osname === 'android'){ return true; }
Next confirm the recipe is running on an iOS device.
if(exports.osname === 'iphone'){
The version is checked to ensure that the device is running iOS 4 or greater. This confirms the operating system supports background processing.
var osVersion = Ti.Platform.version.split("."); //If no iOS 4, then false if(parseInt(osVersion[0],10) < 4){ return false; }
Finally the recipe confirms the device version is greater than the iPhone 3GS. This confirms the hardware supports background processing.
var model = exports.deviceModel.toLoweCase() .replace("iphone","").trim(); var phoneVersion = Ti.Platform.version.split("."); if(parseInt(phoneVersion[0],10) < 3){ return false; } } //Assume modern device return true return true; })();
Universal app development is a common requirement on both Android and iOS. The following helper provides a Boolean indicator if your app is running on a tablet form factor:
//Determine if running on a tablet exports.isTablet = (function() {
Check if the device is either an iPad or an Android device with at least one dimension of 700 pixels or greater.
var tabletCheck = exports.osname === 'ipad' || (exports.osname === 'android' && (!(Math.min( exports.deviceHeight, exports.deviceWidth ) < 700))); return tabletCheck; })();
Note
For Android, this checks if there is a height or width greater than 700 pixels. You may wish to change this based on your targeted devices. For example, if you are targeting some of the larger screen Android phones, you would need to update the default 700 pixels/points to reflect them having a large screen yet still being considered a phone form factor.
With the introduction of the iPhone 5, we need to be aware of two different iPhone screen sizes. The following snippet shows how to create a property indicating if your app is running on an iPhone with this new screen size.
exports.newIPhoneSize = (function(){
First verify the recipe is running on an iPhone device.
if(exports.osname !== 'iphone'){ return false; }
Next check the size to see if there is any dimension greater than 480 points.
return (Math.max( exports.deviceHeight, exports.deviceWidth ) > 480); });
Titanium allows you to create universal apps on iOS. Using the following property, you can branch your code to show different functionalities to your iPad users:
exports.iPad = exports.osname === 'ipad';
The iPad Mini was designed with the same resolution as the first and second generation iPads. Although designed to run iPad apps without modification, the smaller screen size often requires UI adjustments for smaller touch targets. The following code demonstrates how to determine if your app is running on an iPad Mini:
exports.iPadMiniNonRetina= (function() {
Now perform the following steps:
First check if the recipe is running on a nonretina iPad.
if((exports.osname !== 'ipad')|| (exports.osname === 'ipad' && exports.densitylevel==='high')){ return false; }
Next verify if the nonretina iPad is not an iPad 1 or iPad 2. If not either of these modules, assume the recipe is running on an iPad Mini.
var modelToCompare = exports.deviceModel.toLowerCase(); return !( (modelToCompare==="ipad1,1")|| (modelToCompare==="ipad2,1")|| (modelToCompare==="ipad2,2")|| (modelToCompare==="ipad2,3")|| (modelToCompare==="ipad2,4") ); })();
There is built-in ability of Titanium to fire and listen to application wide events. This powerful feature can be a perfect fit for creating loosely-coupled components. This pattern can provide a decoupled logging approach, which is helpful for specific situations such as analytics, logging, and process monitoring modules.
This recipe details how Ti.App.Listener
can be used to create a decoupled re-useable application component. The following screenshot demonstrates this recipe running on both the iPhone and Android platforms:

Adding the CommonJS modules used in this recipe is straightforward and consists of copying the logger.js
and mainWin.js
files into the root of our Titanium project as shown in the following screenshot:

Application-level events allow you to implement a publish/subscribe pattern globally in your app, even across execution contexts. By simply adding a listener such as the following, you can receive messages fired from anywhere in your app:
Ti.App.addEventListener('app:myEvent', myFunctionToHandleThisEvent);
Firing a custom event in Titanium is easy. You simply use the Ti.App.fireEvent
and provide the event name and an optional parameter payload. This example shows how to call the app:myEvent
listener we defined earlier.
Ti.App.fireEvent('app:myEvent',"My parameter objects");
Tip
It is recommended that you name your events using a descriptive convention. For example, app:myEvent
describes that this is an application event and is defined in my app.js
file.
The following recipes show how to use application-level custom events to implement logging across the different contexts of our app.
In the app.js
, we define our application namespace and import the logger and UI CommonJS modules.
//Create our application namespace var my = { ui:{ mainWindow : require('mainWin').createWindow() }, logger : require('logger') };
Next in our app.js
, the setup
method is called on the logger
module. This ensures our database logging tables are created.
//Run setup to create our logging db my.logger.setup();
The application-level listener is then defined in our app.js
. This listener will wait for the app:log
event to be fired and then call the logger's add
method with the payload provided.
//Add a global event to listen for log messages Ti.App.addEventListener('app:log',function(e){ //Provide Log Message to CommonJS Logging Component my.logger.add(e); }); //Open our sample window my.ui.mainWindow.open();
The following code snippet demonstrates how to create the basic CommonJS module, logger.js
, used in our global listener recipe. This module has two methods; setup
, which creates our database objects, and add
, which the listener calls to log our messages.
Create a constant with the logging database name.
var LOG_DB_NAME = "my_log_db";
Create the setup
module-level method. This is used to create the LOG_HISTORY
table, which will later be used to record all log statements.
exports.setup=function(){ var createSQL = 'CREATE TABLE IF NOT EXISTS LOG_HISTORY '; createSQL +='(LOG_NAME TEXT, LOG_MESSAGE TEXT, '; createSQL += 'LOG_DATE DATE)'; //Install the db if needed var db = Ti.Database.open(LOG_DB_NAME); //Create our logging table if needed db.execute(createSQL); //Close the db db.close(); };
Create the add
module-level method. This method is used to record the information to be added to the log table.
exports.add=function(logInfo){ var insertSQL ="INSERT INTO LOG_HISTORY "; insertSQL +=" (LOG_NAME,LOG_MESSAGE,LOG_DATE)"; insertSQL +=" "VALUES(?,?,?)"; var db = Ti.Database.open(LOG_DB_NAME); //Create our logging table if needed db.execute(insertSQL,logInfo.name,logInfo.message, new Date()); //Close the db db.close(); };
The following sections show the contents of our mainWin.js
module. This module returns a window with a single button that when pressed fires our logging event.
The fetchWindow
function returns a simple window demonstrating how to fire an application-level event. To demonstrate, a new message is sent each time, and we add a module level variable named _press_Count
, which increments with each click.
Create the module-level variable _press_Count
to record the number of times addLogButton
has been pressed.
var _press_Count = 0;
The fetchWindow
method returns a Ti.UI.Window
containing the UI elements for this recipe.
exports.fetchWindow=function(){
A Ti.UI.Window
is created for all of our UI elements to be attached.
var win = Ti.UI.createWindow({ backgroundColor:'#fff' });
The addLogButton Ti.UI.Button
is created. This button will be used to trigger the Ti.App.fireEvent
used to demonstrate global listeners in this recipe.
var addLogButton = Ti.UI.createButton({ title:"Fire Log Event", top:180, left:5,right:5, height:50 });
On clicking on the addLogButton
button, the _press_Count
variable is increased by one and a logging object is created. The following code demonstrates how the Ti.App.fireEvent
method is called for the app:log
event and a JavaScript object containing our logging details is provided. The listener we defined in our app.js
will then receive this message and log our results.
addLogButton.addEventListener('click',function(e){
The first action taken after the user taps the addLogButton
is to increase the _pressCount
by one.
_press_Count ++;
Next the logObject
containing information to be submitted for logging is created.
var logObject = { name:'test log', message:'example message ' + _press_Count };
Finally, the Ti.App.fireEvent
is called for the app:log
event. The logObject
is also provided as a parameter when calling this application global event.
Ti.App.fireEvent('app:log',logObject); }); win.add(addLogButton); //Return window return win; };
Since application-level listeners are globally scoped, you need to take caution where and when they are defined. A general rule of thumb is to define application-level listeners in an area of your app that is always available and never reloaded, such as your app.js
, CommonJS file which is only loaded once, or another bootstrapping method.
If you must define them within a window or other module that is loaded repeatedly, make sure you remove the listener when the object is no longer needed. To remove the event listener, you need to call the Ti.App.removeEventListener
method with the same arguments used on creation. The following snippet shows how to remove the app:myEvent
listener created earlier:
Ti.App.removeEventListener('app:myEvent', myFunctionToHandleThisEvent);