In this section, you will explore the ways you can support both Android and iOS within a single Titanium project. Through a combination of platform specific feature abstraction and usage of conditional code branching, Titanium provides you with a powerful mechanism for creating cross platform, native mobile apps with a maximum amount of code reuse.
Titanium is not a write once, run anywhere framework. It's more aptly referred to as a "write once, adapt everywhere" framework. Your business logic, networking, database, and event handling logic will be close to 100% cross-platform compatible. The user interfaces on iOS and Android differ so significantly that in most cases you'll have to do at least a little platform specific coding. That said, it's not uncommon for cross platform apps to reuse 80, 90, or even 100% of their UI code as well.
Embrace the platform
Best of breed, native apps take full advantage of the platforms on which they run. Your Titanium apps should do more than just run on iOS and Android. When running on an iOS device, your app should feel like an iOS app. Your Android app should feel like an Android app. By this, we mean apps that:
- Follow platform UI conventions, such as tabs at the top (Android) or bottom (iOS).
- Use hardware-specific features, such as the Android Menu button.
- Use OS-specific controls, such left and right navigation buttons in title bars on iOS.
- Participate in the platform ecosystem, such as using platform-appropriate notification mechanisms.
The best approach when creating cross-platform apps is to develop and test for both iOS and Android right from the start. Designing and developing your app with multiple platforms in mind right away will be significantly more efficient than developing for one, then porting to the next.
Before we get into the strategies you should adopt, let's look at the mechanics of handling cross-platform coding within Titanium. This includes:
- Platform identification
- Recognizing platform-specific APIs and properties
- Handling platform-specific resources
Titanium provides platform-identification properties in the
Ti.Platform namespace that you can use for conditional branching within your code. These are:
Returns the name of the platform returned by the device
Returns an abbreviated identifier of the platform
Returns device model identifier
Platform-specific APIs and properties
Many of the Titanium APIs are separated according to the platform on which they are supported. For example, the Ti.UI.iPhone namespace includes user interface components that are supported on only on the iOS operating system. The same is true of the Ti.UI.iOS, Ti.UI.iPad, and Ti.UI.Android namespaces. By segmenting such platform-specific functionality, we help make it clear what will and won't work on the various platforms.
As you explore the API docs, you will find also many platform-specific properties and values. For example, the Ti.UI.Window object has an Android-specific property called
softInputMode. That property's value must be one of the constants in the Ti.UI.Android namespace. These platform-specific properties are labeled as such. To avoid crashing and errors, don't try to use them on other platforms.
Another concern is platform-specific constants. Explore the API docs and you'll see lots of constants that define button appearance, media file type, and so forth. When these are platform specific, they are generally put into their own sub-namespace. (If they're not in a platform-specific namespace, expect that we'll be moving them there as we clear up platform parity issues.) For example, Titanium.UI.iOS.ANIMATION_CURVE_EASE_IN defines an iOS-specific animation property. Don't use one platform's constants on another platform or your code will throw an error.
Titanium gives you various ways to include platform-specific resources, like images, stylesheets, and scripts, in your project. Titanium uses an "overrides" system to make it easy to use platform-specific resources. Any file in the platform-specific Resources directories (
Resources/mobileweb) will override, or be used in place of, those in the
Resources directory. You don't have to use any special notation in your code to specify that these files should be used.
You can maintain a hierarchy of folders within the Resources or platform-specific folder. For example, let's say you put most of your images into the
Resources/images folder. To include platform-specific overrides, you would duplicate that folder hierarchy in the platform folders. Thus, you'd need to put the images into the
Here's an example that shows this feature in action with a CommonJS require() module. The code simply calls the base name of the file. Titanium grabs the platform-specific version at build time, which you can see in the Android emulator, iPhone simulator, and Chrome running the Mobile Web preview.
ui.js file is named
ui.jss. Add a
id property when defining your UI components. Then, refer to them within the JSS file like you would in a CSS file.
That was a lot of explanation to get to the point of this section. Titanium supports platform-specific JSS files as well. You can have ui.android.jss and ui.iphone.jss files and Titanium will make sure the correct one is applied when you build your app. (Make sure you don't have a ui.jss file, or it will be used rather than the platform-specific versions.)
Strategies and recommendations
Branching in code is useful when your code will be mostly the same across platforms, but vary here and there. Long blocks of if...then code are difficult to read and maintain. Also, excessive branching will slow your app's execution. If you must use this technique, try to group as much code as you can within a branch and defer loading as much as possible to mitigate the performance penalty of branching.
It's best practice to query the platform value once, then store it in a globally accessible variable. Each time you request one of those properties, Titanium has to query the operating system for the value. This "trip across the bridge" takes a few cycles and if used too frequently could possibly slow your program. Something like the following would be more efficient:
Keep in mind the growing list of supported platforms and don't fall prey to coding in an if/else relationship that won't support new platforms. For example, don't do the following:
Platform-specific JS files
Using platform-specific JS files is likely to be most useful when your code is mostly different across platforms. This removes long if...then blocks from your main code. Separating platform-specific code reduces the chances of an error that comes from accidentally using the wrong platform's API or property. However, you'll have to remember to apply changes and fixes to each of the platform-specific files. So this approach could increase your work rather than reduce it.
For simple projects, and those that don't use CommonJS
require(), you can use JSS to create platform-specific layouts. Currently, however, our JSS implementation doesn't work well with
require(). For more complex apps, you might consider using:
- platform-specific JS files where you embed the platform-specific formatting within the component definitions.
- using the "Tweetanium technique" where you define a global style object, in which you store style settings determined with platform-branching code. Then, you use that object's properties when setting styles on your UI components.
Create a new mobile project named Platform Test. Replace all of the code in app.js with:\
Create two new files, one named app.iphone.js and the other named app.android.jss. In them, enter this code:\
- Build the project for both the iPhone and Android simulator/emulator (if your development computer supports both). The labels and colors should be specific to the platforms as set in the stylesheets.
References and Further Reading
In this section, you learned how to support both Android and iOS within a single set of files. You learned programming strategies as well as the ways Titanium eases the process of working with platform-specific resources. In the final section of this chapter, we'll see how we can use Titanium's integrated internationalization capabilities to make out app's global accessible.