Choosing Platform‑Specific Views in Titanium Alloy for iOS and Android
Learn how to keep a single Alloy controller while tailoring layouts for each OS using .ios.xml/.android.xml files and TSS selectors, with a concrete example and verification steps.
21 Aug 2025, 18:48 UTC

The problem: one controller, two different looks
When building a Titanium SDK app with Alloy, you often need a screen that behaves the same on iOS and Android but looks slightly different—perhaps a button needs extra padding on Android or a label uses a system font only available on iOS. Duplicating the entire controller for each platform leads to maintenance overhead, while trying to cram all platform checks into a single view file can make the XML hard to read.
Thesis: let Alloy pick the right view file
Alloy’s convention‑over‑configuration approach solves this cleanly. By naming view files with the suffixes .ios.xml and .android.xml, the Alloy compiler automatically selects the appropriate file during appc run or appc build. The shared controller remains untouched, and you can still use TSS (Alloy styles) with platform selectors for minor tweaks.
How platform‑specific view files work
- Place a generic view file, e.g.,
views/index.xml, for shared structure. - Create
views/index.ios.xmlandviews/index.android.xmlfor OS‑specific layout overrides. - During compilation, Alloy inspects the target platform and copies only the matching suffixed file into the build directory; the generic file is ignored if a platform‑specific version exists.
- The controller (
controllers/index.js) stays the same and can access UI elements via$.bindings as usual.
If you need only a small adjustment—like changing a margin—you can keep a single index.xml and use TSS selectors:
/* styles/index.tss */
"#saveButton" {
width: Ti.UI.SIZE,
height: 44,
}
"#saveButton[platform=android]" {
bottom: 20; /* extra space for Android navigation bar */
}
"#saveButton[platform=ios]" {
top: 20; /* iOS‑specific spacing */
}
Worked example: a “Save” button with platform‑specific layout
Suppose you want a Save button that stretches full width on iOS but leaves a 16‑dp margin on Android.
1. Create the Alloy project (run in a terminal)
appc new --name DemoApp --id com.example.demoapp --alloy cd DemoApp2. Add the shared controller
File:
controllers/index.jsfunction onSave() { alert('Saved!'); } $.index.open();3. Define the platform‑specific views
File:
views/index.ios.xml<Alloy> <Window class="container"> <Button id="saveButton" title="Save" onClick="onSave" /> </Window> </Alloy>File:
views/index.android.xml<Alloy> <Window class="container"> <Button id="saveButton" title="Save" onClick="onSave" left="16" right="16" /> </Window> </Alloy>4. Optional TSS for shared styling
File:
styles/index.tss"#saveButton" { backgroundColor: #007AFF; color: #fff; height: 48; font: { fontSize: 18 }; } "Window" { backgroundColor: #fff; }5. Build and verify
Run the app for each platform and check the build output to confirm the correct view was used.
# iOS appc run -p ios # Android appc run -p androidAfter each run, look inside the generated build directory (e.g.,
build/iphone/build/DemoApp/Resources/alloy/controllers/index.jsor the Android equivalent). The compiled view will reference eitherindex.ios.xmlorindex.android.xmldepending on the target. You can also inspect the running UI: iOS shows the button edge‑to‑edge, Android shows the 16‑dp margins.Trade‑off and limitation
While platform‑specific view files keep controllers clean, they introduce extra files to maintain. If the divergence grows beyond layout tweaks—say, different business logic or event handlers—you may end up with two controllers that are almost duplicates. In that case, consider splitting into separate controllers or using a shared module with platform checks via
Ti.Platform.OS_IOSandTi.Platform.OS_ANDROID. Another limitation is that a typo in the suffix (e.g.,index.iosxml) causes Alloy to silently fall back to the genericindex.xml, so you won’t get an error; you must verify the build output to catch such mistakes.Actionable closing
To adopt this approach in your own project:
- Identify UI elements that differ only in layout or minor styling.
- Create
*.ios.xmland*.android.xmlfiles alongside the shared view.- Keep the controller unchanged; use
$.bindings to interact with UI.- Validate each platform build by inspecting the generated resources or observing the runtime UI.
- If you notice the view file isn’t being picked up, double‑check the suffix spelling and ensure the generic view isn’t unintentionally overriding the platform‑specific one.
By following these steps, you gain a clear separation of concerns: shared logic stays in one place, while platform‑specific presentation lives in clearly named files that the Alloy compiler handles automatically.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.