Let's take a look at the coding principles and application structures to which you should adhere to create high quality Titanium apps. In addition, we'll take a brief look at some community-supplied frameworks that are available to improve the structure and scalability of your Titanium projects.
- Protect the global scope
- Keep your code DRY (http://en.wikipedia.org/wiki/Don't_repeat_yourself)
- Use a single execution context
Now we have the computed value in our
sum variable, and the
i variables are no longer part of the scope as they were contained within the functional scope of our immediate function.
To refresh your memory on creating namespaces to protect the global scope, here's the example we gave back in the Primer chapter:
By default, a Titanium Mobile application has a single execution context in which it runs. Your application's app.js file bootstraps your application and serves as the root context. Your application can have multiple execution contexts (but as we'll show, you generally don't want it to). New execution contexts are typically created by opening a new window that points to an external URL in its
When the window is opened, the script window.js is immediately run in a new execution context. If the preceding code were run in app.js, any variables or function declarations made in app.js would be unavailable within window.js. Try it:
- Create a new Titanium Mobile project.
- In app.js, cut the following text and paste it into a new file named win1.js:
- Then, update the code that defines win1, like this:
- Build your project for the simulator/emulator. You'll receive an error that
win1is undefined in the win1.js file. The
win1variable is defined in app.js, which is a separate context. Let's fix that.
- In win1.js, add this statement at the top:
- Build your project for the simulator/emulator and this time it will run without errors. To pass data into a separate context, you can add custom properties to your window object.
- In app.js, update the code to read:
- Then, in win1.js, update the code to read:
- Build your project for the simulator/emulator. The custom window property you set in app.js is available within the win1.js context and is used as the label's text.
Let's modify the project you just used to demonstrate multiple contexts.
- In win1.js, add this code:
- Back in app.js, scroll down to find the
label2code. This code is in the app.js context, walled off from the win1.js context. We'll add an event listener here to update the text of
label2when label1 is clicked. Add this code after
- Build your project for the simulator/emulator. Switch to tab 2 and click the label. Switch back to tab 1 and the label should now read 'Sent from win1.js'.
Should I use a single context or multiple contexts?
As is often the case in software development, the answer is "that depends". Most of the time, we recommend you use a single execution context for these reasons:
- You can pass complex objects easily within your app's single context — With events, you can pass JSON-serializable objects, but not objects with methods.
- You include your libraries/dependencies only once, since there's only one context — With multiple contexts, each file would need to include your libraries/dependencies, increasing memory usage.
- In most of the projects done by Appcelerator's own Professional Services team on Titanium Mobile, a single execution context with multiple included external files is used. However, there are instances where having multiple execution contexts is useful. For example, in our Kitchen Sink application, it is advantageous to have a 'clean slate' for every API usage example, so we don't have to worry about polluting the global scope and can keep the examples easy.
Let's recap: keep your code DRY, don't pollute the global scope, and strive for a single context app. We offer a couple of techniques you can use to meet these goals:
- Namespaced pattern with self-calling functions
- CommonJS modules
Namespaced pattern with self-calling functions
A technique we teach in our classroom training sessions involves breaking your app into modular pieces, each constructed using self-calling functions which modify a single app namespace. (If you want to be semantically-correct, these are Immediately Invoked Function Expressions, or IIFEs.) This technique lets you divide code into multiple files, each focused on a discrete purpose. As each library is loaded, it modifies the app's namespace object, which is the only variable added to the global namespace. Let's demonstrate this with code:
The ui.js library file contains the code that constructs your app's user interface.
You would add other libraries, as necessary, to round out the functionality of your app. For example, you might put all your database code in a db.js file, your network code in a network.js, and so forth. We recommend you keep namespace variable names short: you'll be typing them a lot. Use function names that describe their purpose and return value. Take
createApplicationWindow() as an example. It "creates" a "window" and the middle part describes what type of window it's creating. The purpose of the function is clear just by reading its name.
A good example of an app that uses this namespaced pattern is Tweetanium Mobile. We encourage you to download that and study the way that app is put together. The "finished" code we supply for our classroom labs generally follow this namespaced pattern as well.
(As presented here, this is not officially an implementation of the formal modular pattern. But it would not be too difficult to adapt the code presented here to match that design pattern.)
- Resources - The Resources directory of a Titanium application, where the user's source code lives before any processing by our build system.
exports- a free variable within a module, to which multiple properties may be added to create a public interface
module.exports- an object within a module, which may be REPLACED by an object representing the public interface to the module
Our specific implementation of the CommonJS Module Specification is based on (and the early implementation on Android taken directly from) that of node.js. While we should not consider our implementation a direct clone of node, we should favor node conventions where possible to foster reuse of modules across both environments.
In order to use a module within Titanium, you must use the
The string passed to
/ corresponds to your app's Resources directory, to make sure Titanium can locate and load your module. The
sayHello(), which would print a name and a welcome message to the console, it would be accessed in this way:
exports to which properties may be added for the public interface of the module. Anything assigned to it will be available in the calling context as a property of the returned object.
Alternately, if the module author wishes to make the exported value from the module an object of their own design and choosing, there is a non-standard (but common, as with node.js) extension to the Module specification which allows for this. The
module.exports object is available within the module file, and may be assigned any value which the developer would like to return from the
require function for their module. This is most commonly used for functions which act as object constructors. The following would be a typical use case for this:
(You should not mix and match usage of
require() module pattern. We encourage you to use this pattern in your Titanium projects.
Some additional CommonJS resources we recommend you check out are:
MVC and other techniques
The techniques we list above might not suit every need, nor satisfy everyone's requirements. Community members have contributed various frameworks and coding techniques. Without endorsing any of them, here's a few of the frameworks you might consider using:
In this activity, you will create a project in which you first implement the self-calling function pattern. Then, you'll refactor the code to implement the CommonJS module pattern.
- In Studio, create a new project. Delete the default content of app.js.
- Using the module/self-calling technique outlined above, write an app that:
- Defines a single, hierarchical namespace such that only one variable is added to the global namespace
- Opens a window with a red background.
- Includes a single label with white text, centered at the top of the window. Use the label text of your choice.
- Includes a method to update the label's text. Use
setTimeout()to update the label with new text after a few second delay.
- If you implement this app in multiple files, add the necessary
Ti.include()statement to import them all.
- Build and test your app in the simulator/emulator.
- Refactor the code to use the CommonJS
require()technique. Your app should still only define a single, hierarchical namespace such that only one variable is added to the global namespace.
- Build and test your app in the simulator/emulator.
References and Further Reading
- Finished code for Part A and Part B
- CommonJS Modules in Titanium (Titanium Mobile best practices guide)
- Module Pattern
- Immediately Invoked Function Expressions