@maccesar/titools 2.2.11 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/README.md +255 -713
  2. package/bin/titools.js +1 -1
  3. package/lib/commands/update.js +5 -6
  4. package/lib/config.js +1 -0
  5. package/lib/downloader.js +10 -0
  6. package/package.json +1 -1
  7. package/skills/alloy-guides/SKILL.md +46 -48
  8. package/skills/alloy-guides/references/CONTROLLERS.md +4 -6
  9. package/skills/alloy-guides/references/MODELS.md +340 -184
  10. package/skills/alloy-guides/references/VIEWS_DYNAMIC.md +3 -4
  11. package/skills/alloy-guides/references/VIEWS_STYLES.md +44 -46
  12. package/skills/alloy-guides/references/VIEWS_WITHOUT_CONTROLLERS.md +4 -5
  13. package/skills/alloy-guides/references/VIEWS_XML.md +51 -17
  14. package/skills/alloy-guides/references/WIDGETS.md +1 -1
  15. package/skills/alloy-howtos/SKILL.md +12 -13
  16. package/skills/alloy-howtos/references/cli_reference.md +40 -41
  17. package/skills/alloy-howtos/references/samples.md +3 -4
  18. package/skills/purgetss/SKILL.md +369 -356
  19. package/skills/purgetss/references/EXAMPLES.md +24 -25
  20. package/skills/purgetss/references/animation-advanced.md +555 -0
  21. package/skills/purgetss/references/animation-system.md +395 -995
  22. package/skills/purgetss/references/apply-directive.md +111 -141
  23. package/skills/purgetss/references/arbitrary-values.md +206 -480
  24. package/skills/purgetss/references/class-categories.md +277 -0
  25. package/skills/purgetss/references/class-index.md +1 -420
  26. package/skills/purgetss/references/cli-commands.md +446 -556
  27. package/skills/purgetss/references/configurable-properties.md +163 -599
  28. package/skills/purgetss/references/custom-rules.md +33 -76
  29. package/skills/purgetss/references/customization-deep-dive.md +319 -518
  30. package/skills/purgetss/references/dynamic-component-creation.md +33 -37
  31. package/skills/purgetss/references/grid-layout.md +42 -371
  32. package/skills/purgetss/references/icon-fonts.md +82 -475
  33. package/skills/purgetss/references/installation-setup.md +159 -331
  34. package/skills/purgetss/references/migration-guide.md +54 -109
  35. package/skills/purgetss/references/opacity-modifier.md +25 -222
  36. package/skills/purgetss/references/performance-tips.md +2 -2
  37. package/skills/purgetss/references/platform-modifiers.md +21 -407
  38. package/skills/purgetss/references/tikit-components.md +237 -104
  39. package/skills/purgetss/references/titanium-resets.md +20 -21
  40. package/skills/purgetss/references/ui-ux-design.md +171 -1702
  41. package/skills/ti-api/SKILL.md +109 -0
  42. package/skills/ti-api/references/api-android.md +675 -0
  43. package/skills/ti-api/references/api-app-platform.md +636 -0
  44. package/skills/ti-api/references/api-core.md +764 -0
  45. package/skills/ti-api/references/api-data-network.md +641 -0
  46. package/skills/ti-api/references/api-media.md +655 -0
  47. package/skills/ti-api/references/api-modules-ble-bluetooth.md +657 -0
  48. package/skills/ti-api/references/api-modules-coremotion-urlsession.md +411 -0
  49. package/skills/ti-api/references/api-modules-map.md +632 -0
  50. package/skills/ti-api/references/api-modules-nfc.md +725 -0
  51. package/skills/ti-api/references/api-modules-social-misc.md +526 -0
  52. package/skills/ti-api/references/api-services.md +700 -0
  53. package/skills/ti-api/references/api-ui-android.md +499 -0
  54. package/skills/ti-api/references/api-ui-extras.md +702 -0
  55. package/skills/ti-api/references/api-ui-ios-animator.md +378 -0
  56. package/skills/ti-api/references/api-ui-ios.md +756 -0
  57. package/skills/ti-api/references/api-ui-lists.md +581 -0
  58. package/skills/ti-api/references/api-ui-text-input.md +607 -0
  59. package/skills/ti-api/references/api-ui-views.md +572 -0
  60. package/skills/ti-api/references/api-ui-windows-navigation.md +676 -0
  61. package/skills/ti-api/references/api-xml-global.md +743 -0
  62. package/skills/ti-expert/SKILL.md +106 -138
  63. package/skills/ti-expert/references/adaptive-layouts.md +414 -0
  64. package/skills/ti-expert/references/alloy-builtins.md +16 -20
  65. package/skills/ti-expert/references/alloy-structure.md +40 -42
  66. package/skills/ti-expert/references/anti-patterns.md +34 -0
  67. package/skills/ti-expert/references/architecture-tiers.md +248 -0
  68. package/skills/ti-expert/references/code-conventions.md +5 -3
  69. package/skills/ti-expert/references/error-handling.md +14 -7
  70. package/skills/ti-expert/references/examples.md +4 -2
  71. package/skills/ti-expert/references/performance-optimization.md +26 -24
  72. package/skills/ti-expert/references/security-device.md +17 -14
  73. package/skills/ti-expert/references/security-fundamentals.md +60 -101
  74. package/skills/ti-expert/references/state-management.md +15 -14
  75. package/skills/ti-expert/references/testing-e2e-ci.md +30 -21
  76. package/skills/ti-expert/references/theming.md +12 -20
  77. package/skills/ti-guides/SKILL.md +13 -17
  78. package/skills/ti-guides/references/advanced-data-and-images.md +30 -10
  79. package/skills/ti-guides/references/application-frameworks.md +3 -3
  80. package/skills/ti-guides/references/coding-best-practices.md +31 -2
  81. package/skills/ti-guides/references/commonjs-advanced.md +46 -4
  82. package/skills/ti-guides/references/hello-world.md +9 -13
  83. package/skills/ti-guides/references/hyperloop-native-access.md +4 -1
  84. package/skills/ti-guides/references/javascript-primer.md +13 -5
  85. package/skills/ti-guides/references/resources.md +0 -2
  86. package/skills/ti-guides/references/style-and-conventions.md +1 -0
  87. package/skills/ti-guides/references/tiapp-config.md +0 -32
  88. package/skills/ti-howtos/SKILL.md +43 -45
  89. package/skills/ti-howtos/references/buffer-codec-streams.md +25 -3
  90. package/skills/ti-howtos/references/debugging-profiling.md +14 -25
  91. package/skills/ti-howtos/references/google-maps-v2.md +3 -3
  92. package/skills/ti-howtos/references/ios-map-kit.md +8 -1
  93. package/skills/ti-howtos/references/notification-services.md +0 -1
  94. package/skills/ti-howtos/references/webpack-build-pipeline.md +3 -0
  95. package/skills/ti-ui/SKILL.md +47 -49
  96. package/skills/ti-ui/references/application-structures.md +3 -2
  97. package/skills/ti-ui/references/orientation.md +17 -9
  98. package/skills/ti-ui/references/platform-ui-ios.md +63 -0
  99. package/skills/ti-ui/references/scrolling-views.md +39 -0
@@ -4,107 +4,90 @@
4
4
 
5
5
  **NEVER store tokens in:** `Ti.App.Properties` (plaintext), localStorage, or files.
6
6
 
7
- **USE platform-specific secure storage:**
7
+ **USE the `ti.identity` module** — it handles iOS Keychain and Android Keystore through a unified API. Both platforms use `Identity.createKeychainItem()`.
8
8
 
9
9
  ```javascript
10
10
  // lib/services/tokenStorage.js
11
+ const Identity = require('ti.identity')
12
+
13
+ // Create a keychainItem once per identifier
14
+ function createItem(identifier) {
15
+ return Identity.createKeychainItem({ identifier })
16
+ }
17
+
11
18
  exports.TokenStorage = {
12
19
  save(token) {
13
- if (Ti.Platform.osname === 'android') {
14
- // Use Android KeyStore
15
- const keyStore = Ti.Android.createKeyStore({
16
- name: 'SecureKeyStore'
17
- })
18
- keyStore.addEntry('authToken', token)
19
- } else {
20
- // Use iOS Keychain
21
- Ti.KeychainItem.setItem({
22
- identifier: 'authToken',
23
- value: token,
24
- accessGroup: 'com.yourapp.keychain'
20
+ return new Promise((resolve, reject) => {
21
+ const item = createItem('authToken')
22
+ item.addEventListener('save', (e) => {
23
+ e.success ? resolve() : reject(new Error(e.error))
25
24
  })
26
- }
25
+ item.save(token)
26
+ })
27
27
  },
28
28
 
29
29
  get() {
30
- if (Ti.Platform.osname === 'android') {
31
- const keyStore = Ti.Android.createKeyStore({
32
- name: 'SecureKeyStore'
30
+ return new Promise((resolve, reject) => {
31
+ const item = createItem('authToken')
32
+ item.addEventListener('read', (e) => {
33
+ e.success ? resolve(e.value) : reject(new Error(e.error))
33
34
  })
34
- return keyStore.getEntry('authToken')
35
- } else {
36
- return Ti.KeychainItem.getItem({
37
- identifier: 'authToken',
38
- accessGroup: 'com.yourapp.keychain'
39
- })
40
- }
35
+ item.read()
36
+ })
41
37
  },
42
38
 
43
39
  clear() {
44
- if (Ti.Platform.osname === 'android') {
45
- const keyStore = Ti.Android.createKeyStore({
46
- name: 'SecureKeyStore'
47
- })
48
- keyStore.removeEntry('authToken')
49
- } else {
50
- Ti.KeychainItem.removeItem({
51
- identifier: 'authToken',
52
- accessGroup: 'com.yourapp.keychain'
40
+ return new Promise((resolve, reject) => {
41
+ const item = createItem('authToken')
42
+ item.addEventListener('reset', (e) => {
43
+ e.success ? resolve() : reject(new Error(e.error))
53
44
  })
54
- }
45
+ item.reset()
46
+ })
55
47
  }
56
48
  }
57
49
  ```
58
50
 
51
+ > Verified against official `ti.identity` module docs. `Identity.createKeychainItem({identifier})` is the cross-platform API — it maps to iOS Keychain and Android Keystore automatically. `Ti.Android.createKeyStore()` and `Ti.KeychainItem.setItem()` do NOT exist.
52
+
59
53
  ## Certificate pinning
60
54
 
61
- Prevent man-in-the-middle attacks by pinning SSL certificates:
55
+ Prevent man-in-the-middle attacks by pinning SSL certificates using the **`ti.https` module** (community module — not built into the SDK):
62
56
 
63
57
  ```javascript
64
58
  // lib/api/pinnedClient.js
65
- exports.createPinnedClient = function() {
66
- const client = Ti.Network.createHTTPClient({
67
- // Security: Enable certificate pinning
68
- certificatePinning: true,
59
+ // Requires: ti.https module install via npm or tiapp.xml modules section
60
+ const HTTPS = require('ti.https')
61
+
62
+ const securityManager = HTTPS.createX509CertificatePinningSecurityManager([
63
+ {
64
+ url: 'https://api.example.com',
65
+ serverCertificate: Ti.Filesystem.getFile(
66
+ Ti.Filesystem.resourcesDirectory, 'certificates/api-pin.pem'
67
+ ).read()
68
+ }
69
+ ])
69
70
 
70
- // Specify allowed certificates
71
+ exports.createPinnedClient = function(options = {}) {
72
+ return Ti.Network.createHTTPClient({
73
+ securityManager, // must be set at creation time
71
74
  validatesSecureCertificate: true,
72
-
73
- onload: () => {
74
- // Success
75
- },
76
-
77
- onerror: (e) => {
78
- // Certificate validation failed
79
- if (e.error.indexOf('certificate') >= 0) {
80
- Ti.API.error('Certificate pinning failed - possible MITM attack')
81
- }
82
- }
75
+ timeout: 10000,
76
+ ...options
83
77
  })
84
-
85
- return client
86
78
  }
87
79
  ```
88
80
 
89
- **Add certificates to tiapp.xml:**
90
-
91
- ```xml
92
- <ti:app>
93
- <certificates>
94
- <certificate>
95
- <name>api.example.com</name>
96
- <type>rsa</type>
97
- <file>certificates/api-pin.pem</file>
98
- </certificate>
99
- </certificates>
100
- </ti:app>
101
- ```
81
+ > `certificatePinning: true` is NOT a valid `HTTPClient` property. Certificate pinning requires the `ti.https` module and must be set via the `securityManager` property when creating the client. Note that the `validatesSecureCertificate` property of `HTTPClient` is not honored for pinned URLs — the security manager takes precedence.
102
82
 
103
83
  ## Data encryption at rest
104
84
 
85
+ > **Community pattern** — `ti.crypto` is a third-party community module (not part of the Titanium SDK core). Verify availability and compatibility before using in production.
86
+
105
87
  ```javascript
106
88
  // lib/services/encryption.js
107
89
  // AES-256 encryption for sensitive local data
90
+ // Requires: ti.crypto community module
108
91
 
109
92
  const crypto = require('ti.crypto')
110
93
 
@@ -125,30 +108,6 @@ exports.decrypt = function(encryptedData, key) {
125
108
  options: { mode: crypto.CBC }
126
109
  })
127
110
  }
128
-
129
- // Usage: Secure cache of sensitive user data
130
- module.exports = class SecureCache {
131
- constructor(encryptionKey) {
132
- this.key = encryptionKey
133
- this.cache = {}
134
- }
135
-
136
- set(key, value) {
137
- const encrypted = encrypt(JSON.stringify(value), this.key)
138
- this.cache[key] = encrypted
139
- }
140
-
141
- get(key) {
142
- if (!this.cache[key]) return null
143
-
144
- const decrypted = decrypt(this.cache[key], this.key)
145
- return JSON.parse(decrypted)
146
- }
147
-
148
- clear() {
149
- this.cache = {}
150
- }
151
- }
152
111
  ```
153
112
 
154
113
  ## Secure HTTP communication
@@ -270,15 +229,15 @@ exports.Validator = {
270
229
 
271
230
  ## Owasp mobile security checklist
272
231
 
273
- | Category | Check | Implementation |
232
+ | Category | Check | Implementation |
274
233
  | -------------------- | --------------------------- | ---------------------------------- |
275
- | **Data Storage** | Credentials stored securely | Keychain/KeyStore for tokens |
276
- | **Data Storage** | Sensitive data encrypted | AES-256 for cached data |
277
- | **Communication** | HTTPS only | `validatesSecureCertificate: true` |
278
- | **Communication** | Certificate pinning | SSL pinning enabled |
279
- | **Authentication** | Token refresh | Auto-refresh before expiry |
280
- | **Authentication** | Session timeout | Auto-logout after inactivity |
281
- | **Input Validation** | Server-side validation | Never trust client input |
282
- | **Input Validation** | Sanitize user input | Remove XSS patterns |
283
- | **Cryptography** | No hardcoded keys | Keys from secure storage |
284
- | **Cryptography** | Use standard algorithms | AES-256, SHA-256 |
234
+ | **Data Storage** | Credentials stored securely | Keychain/KeyStore for tokens |
235
+ | **Data Storage** | Sensitive data encrypted | AES-256 for cached data |
236
+ | **Communication** | HTTPS only | `validatesSecureCertificate: true` |
237
+ | **Communication** | Certificate pinning | SSL pinning enabled |
238
+ | **Authentication** | Token refresh | Auto-refresh before expiry |
239
+ | **Authentication** | Session timeout | Auto-logout after inactivity |
240
+ | **Input Validation** | Server-side validation | Never trust client input |
241
+ | **Input Validation** | Sanitize user input | Remove XSS patterns |
242
+ | **Cryptography** | No hardcoded keys | Keys from secure storage |
243
+ | **Cryptography** | Use standard algorithms | AES-256, SHA-256 |
@@ -20,6 +20,7 @@ Create a single source of truth using Backbone.Events:
20
20
  ```javascript
21
21
  // lib/services/stateStore.js
22
22
  const Backbone = require('alloy/backbone')
23
+ const _ = require('alloy/underscore')._
23
24
 
24
25
  class StateStore {
25
26
  constructor() {
@@ -280,13 +281,13 @@ Ti.App.addEventListener('resume', () => {
280
281
 
281
282
  ## Anti-patterns
282
283
 
283
- | Anti-Pattern | Why It's Bad | Solution |
284
+ | Anti-Pattern | Why It's Bad | Solution |
284
285
  | ------------------------------------- | ---------------------------- | ---------------------------------------- |
285
- | `Ti.App.fireEvent` for state | No cleanup, memory leaks | Use StateStore with `offChange` |
286
- | Direct collection mutation | Bypasses reactivity | Use collection methods (`add`, `remove`) |
287
- | State in multiple places | Inconsistency bugs | Single source of truth |
288
- | Global variables (`Alloy.Globals`) | No reactivity, hard to track | Use StateStore |
289
- | Controller-to-controller direct calls | Tight coupling | Use StateStore or events |
286
+ | `Ti.App.fireEvent` for state | No cleanup, memory leaks | Use StateStore with `offChange` |
287
+ | Direct collection mutation | Bypasses reactivity | Use collection methods (`add`, `remove`) |
288
+ | State in multiple places | Inconsistency bugs | Single source of truth |
289
+ | Global variables (`Alloy.Globals`) | No reactivity, hard to track | Use StateStore |
290
+ | Controller-to-controller direct calls | Tight coupling | Use StateStore or events |
290
291
 
291
292
  ## Persistence strategies
292
293
 
@@ -472,14 +473,14 @@ exports.appStore = {
472
473
 
473
474
  ### Choosing a strategy
474
475
 
475
- | Data Type | Strategy | Reason |
476
- | ------------------ | ------------------- | ---------------------- |
477
- | User preferences | Ti.App.Properties | Simple main-value, fast |
478
- | Auth tokens | Keychain/KeyStore | Security |
479
- | User profile | Properties + Secure | Mixed sensitivity |
480
- | Lists (100+ items) | SQLite | Query, pagination |
481
- | Offline queue | SQLite | Durability, FIFO |
482
- | Cache | In-memory + SQLite | Speed + persistence |
476
+ | Data Type | Strategy | Reason |
477
+ | ------------------ | ------------------- | ----------------------- |
478
+ | User preferences | Ti.App.Properties | Simple main-value, fast |
479
+ | Auth tokens | Keychain/KeyStore | Security |
480
+ | User profile | Properties + Secure | Mixed sensitivity |
481
+ | Lists (100+ items) | SQLite | Query, pagination |
482
+ | Offline queue | SQLite | Durability, FIFO |
483
+ | Cache | In-memory + SQLite | Speed + persistence |
483
484
 
484
485
  ## State middleware
485
486
 
@@ -127,21 +127,30 @@ describe('Login Flow', () => {
127
127
 
128
128
  ## Adding accessibility IDs for testing
129
129
 
130
+ Appium uses accessibility identifiers to locate elements. The correct properties are:
131
+ - **iOS**: `accessibilityIdentifier` — maps to `$('~id')` in WebdriverIO
132
+ - **Android**: `contentDescription` — also maps to `$('~id')` in WebdriverIO
133
+
134
+ `testId` is NOT an official Titanium view property. Use `accessibilityIdentifier` instead.
135
+
130
136
  ```xml
131
137
  <!-- views/auth/login.xml -->
132
- <Window testId="loginScreen">
133
- <TextField id="emailField" testId="emailField" />
134
- <TextField id="passwordField" testId="passwordField" />
135
- <Button id="loginBtn" testId="loginButton" />
136
- <Label id="errorLabel" testId="errorLabel" />
138
+ <Window id="loginWindow" accessibilityIdentifier="loginScreen">
139
+ <TextField id="emailField" accessibilityIdentifier="emailField" />
140
+ <TextField id="passwordField" accessibilityIdentifier="passwordField" />
141
+ <Button id="loginBtn" accessibilityIdentifier="loginButton" />
142
+ <Label id="errorLabel" accessibilityIdentifier="errorLabel" />
137
143
  </Window>
138
144
  ```
139
145
 
140
146
  ```javascript
141
- // In controller or alloy.js - map testId to accessibilityLabel
142
- if (Alloy.CFG.debug) {
143
- // Auto-set accessibilityLabel from testId during development
144
- Ti.UI.defaultUnit = 'dp'
147
+ // For Android, also set contentDescription to match the same value
148
+ // since accessibilityIdentifier is iOS-specific.
149
+ // In controller:
150
+ if (OS_ANDROID) {
151
+ $.emailField.contentDescription = 'emailField'
152
+ $.passwordField.contentDescription = 'passwordField'
153
+ $.loginBtn.contentDescription = 'loginButton'
145
154
  }
146
155
  ```
147
156
 
@@ -412,21 +421,21 @@ end
412
421
 
413
422
  Configure in GitHub repository settings:
414
423
 
415
- | Rule | Setting |
424
+ | Rule | Setting |
416
425
  | --------------------------------- | ------------------------------------------ |
417
- | Require pull request reviews | 1 approval required |
418
- | Require status checks | lint, unit-tests, build-ios, build-android |
419
- | Require branches to be up to date | Yes |
420
- | Include administrators | Yes |
426
+ | Require pull request reviews | 1 approval required |
427
+ | Require status checks | lint, unit-tests, build-ios, build-android |
428
+ | Require branches to be up to date | Yes |
429
+ | Include administrators | Yes |
421
430
 
422
431
  ## Testing best practices summary
423
432
 
424
- | Area | Practice |
433
+ | Area | Practice |
425
434
  | --------------------- | ------------------------------------------ |
426
- | **Unit Tests** | Test business logic in services/helpers |
435
+ | **Unit Tests** | Test business logic in services/helpers |
427
436
  | **Integration Tests** | Test controller flows with mocked services |
428
- | **E2E Tests** | Test critical user journeys |
429
- | **Coverage** | Aim for 80%+ on services, 60%+ overall |
430
- | **CI Pipeline** | Run lint -> unit tests -> build -> E2E |
431
- | **Artifacts** | Save screenshots on failure |
432
- | **Notifications** | Slack/email on build failures |
437
+ | **E2E Tests** | Test critical user journeys |
438
+ | **Coverage** | Aim for 80%+ on services, 60%+ overall |
439
+ | **CI Pipeline** | Run lint -> unit tests -> build -> E2E |
440
+ | **Artifacts** | Save screenshots on failure |
441
+ | **Notifications** | Slack/email on build failures |
@@ -41,9 +41,8 @@ Set the theme in `config.json`:
41
41
  }
42
42
  ```
43
43
 
44
- :::warning Theme is compile-time only
45
- Alloy themes are applied at **build time**. You cannot switch themes at runtime using this system alone. For runtime switching, use the Dynamic Theming approach below.
46
- :::
44
+ > **⚠️ Theme is compile-time only**
45
+ > Alloy themes are applied at **build time**. You cannot switch themes at runtime using this system alone. For runtime switching, use the Dynamic Theming approach below.
47
46
 
48
47
  ### Theme cascade
49
48
 
@@ -178,9 +177,8 @@ exports.ThemeService = {
178
177
 
179
178
  ### Step 4: respond to theme changes in controllers
180
179
 
181
- :::warning Theme change requires UI rebuild
182
- Since TSS is applied at controller creation time, changing `Alloy.Globals.theme` does NOT retroactively update already-rendered views. You must manually update visible elements or restart the root controller.
183
- :::
180
+ > **⚠️ Theme change requires UI rebuild**
181
+ > Since TSS is applied at controller creation time, changing `Alloy.Globals.theme` does NOT retroactively update already-rendered views. You must manually update visible elements or restart the root controller.
184
182
 
185
183
  **Approach A: Rebuild root controller (simplest)**
186
184
 
@@ -245,20 +243,15 @@ exports.ThemeService = {
245
243
 
246
244
  // Initialize system theme listener
247
245
  initSystemListener() {
248
- // Detect current system theme
246
+ // Detect current system theme at startup
249
247
  this.followSystem()
250
248
 
251
- // Listen for system theme changes
252
- Ti.App.addEventListener('significantTimeChange', () => {
249
+ // Re-check on resume (user may have changed system Dark Mode while app was backgrounded)
250
+ // Note: Titanium does not fire a dedicated dark-mode-change event.
251
+ // `Ti.UI.semanticColorType` is the correct way to check the current setting.
252
+ Ti.App.addEventListener('resumed', () => {
253
253
  this.followSystem()
254
254
  })
255
-
256
- // iOS: userInterfaceStyle change
257
- if (OS_IOS) {
258
- Ti.App.addEventListener('traitCollectionChange', () => {
259
- this.followSystem()
260
- })
261
- }
262
255
  }
263
256
  }
264
257
  ```
@@ -322,10 +315,9 @@ $.container.backgroundColor = 'backgroundColor';
322
315
  - Alpha can be set from 0.0-100.0 (integer or float)
323
316
  - Light values can use hex with alpha via ARGB/AARRGGBB format
324
317
 
325
- :::tip When to use Semantic Colors vs Alloy.Globals
326
- - **Semantic Colors**: Built-in Dark Mode support, automatic switching, simpler setup. Works cross-platform.
327
- - **Alloy.Globals palette**: More control over when/how themes switch, easier to add custom themes beyond light/dark, runtime theme changes with UI rebuild.
328
- :::
318
+ > **💡 When to use Semantic Colors vs Alloy.Globals**
319
+ > - **Semantic Colors**: Built-in Dark Mode support, automatic switching, simpler setup. Works cross-platform.
320
+ > - **Alloy.Globals palette**: More control over when/how themes switch, easier to add custom themes beyond light/dark, runtime theme changes with UI rebuild.
329
321
 
330
322
  ---
331
323
 
@@ -11,20 +11,19 @@ Use this skill to keep Titanium projects aligned with TiDev standards for stabil
11
11
 
12
12
  ## Project detection
13
13
 
14
- :::info Auto-detects Titanium projects
15
- This skill auto-detects Titanium projects. No manual command is needed.
16
-
17
- Titanium project indicator:
18
- - `tiapp.xml` file (definitive indicator)
19
-
20
- Applies to both:
21
- - Alloy projects (`app/` folder)
22
- - Classic projects (`Resources/` folder)
23
-
24
- Behavior:
25
- - If a Titanium project is detected, provide official Titanium SDK guidance, memory management best practices, and app distribution help.
26
- - If not detected, say this skill is only for Titanium projects.
27
- :::
14
+ > **️ℹ️ Auto-detects Titanium projects**
15
+ > This skill auto-detects Titanium projects. No manual command is needed.
16
+ >
17
+ > Titanium project indicator:
18
+ > - `tiapp.xml` file (definitive indicator)
19
+ >
20
+ > Applies to both:
21
+ > - Alloy projects (`app/` folder)
22
+ > - Classic projects (`Resources/` folder)
23
+ >
24
+ > Behavior:
25
+ > - If a Titanium project is detected, provide official Titanium SDK guidance, memory management best practices, and app distribution help.
26
+ > - If not detected, say this skill is only for Titanium projects.
28
27
 
29
28
  ## Core workflow
30
29
 
@@ -52,9 +51,6 @@ Behavior:
52
51
  - Hyperloop Native Access (references/hyperloop-native-access.md): Objective-C/Swift/Java syntax, casting, debugging, XIB/Storyboards.
53
52
  - Style & Conventions (references/style-and-conventions.md): naming standards and formatting rules.
54
53
  - Reserved Words (references/reserved-words.md): ECMAScript, iOS, and Alloy reserved keywords to avoid.
55
- - Alloy CLI Reference (references/alloy-cli-advanced.md): extract-i18n, code generation, and build hooks.
56
- - Alloy Data Mastery (references/alloy-data-mastery.md): sync adapters, data binding, and Backbone collections.
57
- - Alloy Widgets & Themes (references/alloy-widgets-and-themes.md): widget structure, styling priorities, and theming.
58
54
  - Android Manifest (references/android-manifest.md): custom AndroidManifest.xml, permissions, and manifest merge.
59
55
  - App Distribution (references/app-distribution.md): Google Play (APK/AAB), App Store (IPA), certificates, provisioning, and deployment.
60
56
  - tiapp.xml Configuration (references/tiapp-config.md): complete reference for tiapp.xml and timodule.xml, including all elements, properties, and platform-specific settings.
@@ -15,6 +15,26 @@
15
15
  ```
16
16
  - Do not ship large pre-populated databases. Ship a small skeleton and download data on first boot to reduce IPA/APK size. The `Resources` directory is read-only, so installing a database copies it to `applicationDataDirectory`, which creates two copies on the device.
17
17
  - Android note: in Android 2.2 and earlier, the installer could not uncompress assets over 1 MB. One workaround was to rename the file with a `.mp3` extension to prevent `aapt` compression.
18
+ - Pattern for downloading a replacement database on first boot:
19
+ ```javascript
20
+ const updateDatabase = (newData) => {
21
+ // delete existing content and rehydrate from downloaded JSON
22
+ }
23
+ downloadButton.addEventListener('click', () => {
24
+ const client = Ti.Network.createHTTPClient({
25
+ timeout: 10000,
26
+ onload() {
27
+ updateDatabase(this.responseData)
28
+ Ti.UI.createAlertDialog({ title: 'Info', message: 'Database installed' }).show()
29
+ },
30
+ onerror(e) {
31
+ Ti.UI.createAlertDialog({ title: 'Error', message: e.error }).show()
32
+ }
33
+ })
34
+ client.open('GET', 'https://example.com/data.json')
35
+ client.send()
36
+ })
37
+ ```
18
38
 
19
39
  ### Database version management
20
40
 
@@ -45,11 +65,11 @@ const addColumn = (dbname, tblName, newFieldName, colSpec) => {
45
65
 
46
66
  Choose the right format for each use case:
47
67
 
48
- | Format | Compression | Best for | Avoid for |
49
- | --- | --- | --- | --- |
50
- | PNG | Lossless | Icons, text, line art, buttons | Photos (large files) |
51
- | JPG | Lossy | Photographs | Text, icons, line drawings (artifacts) |
52
- | GIF | Lossless (256 colors) | Rare cases | Most cases (limited colors, animated GIF not supported everywhere) |
68
+ | Format | Compression | Best for | Avoid for |
69
+ | ------ | --------------------- | ------------------------------ | ------------------------------------------------------------------ |
70
+ | PNG | Lossless | Icons, text, line art, buttons | Photos (large files) |
71
+ | JPG | Lossy | Photographs | Text, icons, line drawings (artifacts) |
72
+ | GIF | Lossless (256 colors) | Rare cases | Most cases (limited colors, animated GIF not supported everywhere) |
53
73
 
54
74
  For flip-book animations, use `ImageView.images` with an array of PNG or optimized JPG files instead of animated GIFs.
55
75
 
@@ -80,12 +100,12 @@ For flip-book animations, use `ImageView.images` with an array of PNG or optimiz
80
100
 
81
101
  Resize and compress images before including them in your app to reduce IPA/APK size and network usage.
82
102
 
83
- | Platform | File types | Tool |
84
- | --- | --- | --- |
85
- | Mac | PNG, JPG, GIF | ImageOptim |
103
+ | Platform | File types | Tool |
104
+ | ------------------- | ------------- | ----------- |
105
+ | Mac | PNG, JPG, GIF | ImageOptim |
86
106
  | Mac, Windows, Linux | PNG, JPG, GIF | ImageMagick |
87
- | Windows/DOS | PNG | PNGCrush |
88
- | Windows | JPG | Nikkho |
107
+ | Windows/DOS | PNG | PNGCrush |
108
+ | Windows | JPG | Nikkho |
89
109
 
90
110
  ---
91
111
 
@@ -118,11 +118,11 @@ app.js:
118
118
  // Single namespace pattern
119
119
  const app = {};
120
120
 
121
- // Include components (Legacy)
122
- Ti.include('ui/ApplicationWindow.js');
121
+ // Load components via require (Ti.include is deprecated)
122
+ const AppWindow = require('ui/ApplicationWindow');
123
123
 
124
124
  // Create and open window
125
- app.mainWindow = app.ui.createApplicationWindow();
125
+ app.mainWindow = AppWindow.createApplicationWindow();
126
126
  app.mainWindow.open();
127
127
  ```
128
128
 
@@ -34,6 +34,8 @@ Ti.App.fireEvent('my_event')
34
34
 
35
35
  - Defer script loading. Only `require` modules when you need them.
36
36
 
37
+ Defer loading to reduce startup time. Only `require` modules when you need them.
38
+
37
39
  Lazy script loading example:
38
40
  ```javascript
39
41
  // must be loaded at launch
@@ -56,8 +58,16 @@ win1.addEventListener('click', () => {
56
58
  });
57
59
  ```
58
60
 
59
- - Bridge efficiency: minimize requests for device properties like `Ti.Platform.osname`. Store them in a local variable.
60
- - Avoid extending the `Ti` namespace. `Ti` objects are proxies to native components. Properties set on them may not reach the native object, arrays return copies, and persistence across SDK versions is not guaranteed. Use modules instead.
61
+ - Bridge efficiency: minimize requests for device properties like `Ti.Platform.osname`. Store them in a local variable:
62
+ ```javascript
63
+ const isAndroid = (Ti.Platform.osname === 'android')
64
+ if (isAndroid) { /* Android path */ } else { /* iOS path */ }
65
+ ```
66
+ - Avoid extending the `Ti` namespace. Three reasons:
67
+ 1. Ti objects are proxies to native OS components — your extensions may conflict with native functionality.
68
+ 2. Arrays stored on the namespace may be immutable; objects may be null unexpectedly.
69
+ 3. There is no guarantee properties persist across SDK versions.
70
+ Use your own namespace or CommonJS module instead of modifying `Ti`.
61
71
 
62
72
  ## 5. App architecture recommendations
63
73
 
@@ -85,6 +95,25 @@ const myModule = require('/MyModule');
85
95
  myModule.sayHello('User');
86
96
  ```
87
97
 
98
+ ### Namespace with deferred loading (alternative pattern)
99
+
100
+ If not using CommonJS, you can still defer loading with a namespace:
101
+
102
+ ```javascript
103
+ const someNameSpace = () => {
104
+ const API = {
105
+ init() {
106
+ // create UI or initialize
107
+ },
108
+ reset() {
109
+ // null objects, clean up
110
+ }
111
+ }
112
+ return API
113
+ }
114
+ const ns = new someNameSpace()
115
+ ```
116
+
88
117
  ### Custom objects as components
89
118
 
90
119
  Popular for rapid deployment. It uses a namespace hierarchy. This pattern is less efficient than CommonJS modules, and object references can persist after they are no longer needed.
@@ -162,7 +162,49 @@ const logger = require('logger')
162
162
  logger.info('some log statement with a timestamp')
163
163
  ```
164
164
 
165
- ## 7. Inter-module state sharing
165
+ ## 7. Packages of related functionality
166
+
167
+ Group related classes or helpers in a single module:
168
+
169
+ ```javascript
170
+ // app/lib/geo.js
171
+ class Point {
172
+ constructor(x, y) {
173
+ this.x = x
174
+ this.y = y
175
+ }
176
+ }
177
+
178
+ class Line {
179
+ constructor(start, end) {
180
+ this.start = start
181
+ this.end = end
182
+ }
183
+
184
+ slope() {
185
+ return (this.end.y - this.start.y) / (this.end.x - this.start.x)
186
+ }
187
+
188
+ yIntercept() {
189
+ return this.start.y - (this.slope() * this.start.x)
190
+ }
191
+ }
192
+
193
+ exports.Point = Point
194
+ exports.Line = Line
195
+ ```
196
+
197
+ Usage:
198
+ ```javascript
199
+ const Geo = require('lib/geo')
200
+
201
+ const start = new Geo.Point(1, -5)
202
+ const end = new Geo.Point(10, 2)
203
+ const line = new Geo.Line(start, end)
204
+ Ti.API.info(line.slope())
205
+ ```
206
+
207
+ ## 8. Inter-module state sharing
166
208
 
167
209
  When a module assigns a primitive value to `exports`, the consumer gets a copy, not a live reference. Changes to the internal variable are not reflected in the exported property.
168
210
 
@@ -190,7 +232,7 @@ Ti.API.info(stateful.stepVal) // 5 - still the original copy
190
232
 
191
233
  Rule: use getter/setter functions for stateful values. Direct property exports of primitives are snapshots at module load time.
192
234
 
193
- ## 8. Antipatterns to avoid
235
+ ## 9. Antipatterns to avoid
194
236
 
195
237
  ### Do not assign directly to `exports`
196
238
 
@@ -217,7 +259,7 @@ exports.foo = 'bar'
217
259
 
218
260
  Any data a module needs should be passed during construction or initialization. Avoid globals shared across modules.
219
261
 
220
- ## 9. Security and scope
262
+ ## 10. Security and scope
221
263
 
222
264
  All modules have private scope. Variables declared within the module are private unless added to `exports`.
223
265
 
@@ -230,7 +272,7 @@ exports.publicMethod = () => {
230
272
  }
231
273
  ```
232
274
 
233
- ## 10. Node.js compatibility
275
+ ## 11. Node.js compatibility
234
276
 
235
277
  Titanium supports Node.js module patterns and `require()` resolution. Node.js modules can often be used directly.
236
278