@maccesar/titools 4.4.0 → 4.5.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.
- package/README.md +10 -13
- package/package.json +1 -1
- package/skills/purgetss/SKILL.md +4 -9
- package/skills/ti-api/references/api-core.md +24 -69
- package/skills/ti-api/references/api-data-network.md +18 -63
- package/skills/ti-api/references/api-media.md +225 -107
- package/skills/ti-api/references/api-modules-coremotion-urlsession.md +6 -20
- package/skills/ti-api/references/api-modules-map.md +45 -58
- package/skills/ti-api/references/api-modules-nfc.md +23 -56
- package/skills/ti-api/references/api-modules-social-misc.md +23 -3
- package/skills/ti-api/references/api-ui-android.md +16 -37
- package/skills/ti-api/references/api-ui-extras.md +106 -87
- package/skills/ti-api/references/api-ui-lists.md +103 -57
- package/skills/ti-api/references/api-ui-text-input.md +13 -39
- package/skills/ti-api/references/api-ui-windows-navigation.md +95 -93
- package/skills/ti-api/references/api-xml-global.md +25 -60
- package/skills/ti-expert/SKILL.md +8 -11
- package/skills/ti-expert/references/adaptive-layouts.md +15 -43
- package/skills/ti-expert/references/alloy-builtins.md +1 -4
- package/skills/ti-expert/references/alloy-structure.md +4 -12
- package/skills/ti-expert/references/anti-patterns.md +16 -45
- package/skills/ti-expert/references/code-conventions.md +2 -6
- package/skills/ti-expert/references/controller-patterns.md +1 -3
- package/skills/ti-expert/references/examples.md +3 -10
- package/skills/ti-expert/references/feedback-migration.md +17 -39
- package/skills/ti-expert/references/feedback-surfaces.md +19 -48
- package/skills/ti-expert/references/feedback-widget-contracts.md +31 -69
- package/skills/ti-expert/references/file-type-association.md +265 -0
- package/skills/ti-expert/references/migration-patterns.md +8 -18
- package/skills/ti-expert/references/sharing.md +227 -0
- package/skills/ti-expert/references/theming.md +20 -45
- package/skills/ti-guides/SKILL.md +5 -1
- package/skills/ti-guides/references/coding-best-practices.md +1 -2
- package/skills/ti-guides/references/compatibility-matrix.md +89 -0
- package/skills/ti-guides/references/resources.md +7 -1
- package/skills/ti-guides/references/sdk-release-notes.md +75 -0
- package/skills/ti-guides/references/style-and-conventions.md +1 -2
- package/skills/ti-howtos/references/debugging-profiling.md +1 -2
- package/skills/ti-howtos/references/location-and-maps.md +1 -2
- package/skills/ti-howtos/references/using-modules.md +1 -2
- package/skills/ti-howtos/references/web-content-integration.md +1 -2
- package/skills/ti-howtos/references/webpack-build-pipeline.md +1 -2
- package/skills/ti-ui/references/listviews-and-performance.md +5 -10
- package/skills/ti-ui/references/orientation.md +1 -2
- package/skills/ti-ui/references/scrolling-views.md +7 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: ti-expert
|
|
3
|
-
description: "Use when designing, reviewing, auditing,
|
|
3
|
+
description: "Use when designing, reviewing, auditing, or analyzing Titanium SDK architecture and implementation (Alloy or Classic) — creating controllers/views/services, choosing models vs collections, deciding between app-owned and system UI, designing reusable Widgets or feedback/modality patterns, hunting memory leaks and cleanup gaps, testing, migrating legacy apps, building adaptive layouts for tablets, foldables and large screens, or moving documents in and out of the app: custom file types, UTIs, CFBundleDocumentTypes, Android intent filters, AirDrop/Quick Share, document icons, sharing a link or file via the iOS share sheet or Android intent chooser. AUTO-DETECT: If tiapp.xml exists, invoke BEFORE architectural decisions, new controllers/views, feedback components, or restructuring. Titanium navigation, memory, event, and modal patterns differ from web frameworks."
|
|
4
4
|
argument-hint: "[architecture-topic]"
|
|
5
5
|
allowed-tools: Read, Grep, Glob, Edit, Write, Bash(git *), Bash(node *)
|
|
6
6
|
---
|
|
@@ -11,8 +11,7 @@ Practical architecture and implementation guidance for Titanium SDK apps (Alloy
|
|
|
11
11
|
|
|
12
12
|
## Required workflow
|
|
13
13
|
|
|
14
|
-
This file is an index. Read the references that govern the task before making a
|
|
15
|
-
recommendation or changing code.
|
|
14
|
+
This file is an index. Read the references that govern the task before making a recommendation or changing code.
|
|
16
15
|
|
|
17
16
|
| Task | Required reading |
|
|
18
17
|
| --- | --- |
|
|
@@ -22,12 +21,12 @@ recommendation or changing code.
|
|
|
22
21
|
| Migrating native dialogs, Toasts, or local overlays | [Feedback migration](references/feedback-migration.md), [Migration patterns](references/migration-patterns.md) |
|
|
23
22
|
| Theme or dark mode | [Theming](references/theming.md); if PurgeTSS is detected, invoke the `purgetss` skill before styling |
|
|
24
23
|
| Tablet, foldable, large screen, or responsive behavior | [Adaptive layouts](references/adaptive-layouts.md) |
|
|
24
|
+
| Opening or receiving files: custom extensions, UTIs, intent filters, AirDrop, Quick Share, document icons | [File type association](references/file-type-association.md) |
|
|
25
|
+
| Sending content out: share sheet, intent chooser, sharing a link or a file, `dk.napp.social`, `com.alcoapps.socialshare` | [Sharing content out](references/sharing.md) |
|
|
25
26
|
| Security architecture | [Security fundamentals](references/security-fundamentals.md), [Device security](references/security-device.md) |
|
|
26
27
|
| Testing or performance | Load the matching guide under Quality & Performance below |
|
|
27
28
|
|
|
28
|
-
Support recommendations with `[source: references/<file>.md]`. If a claim cannot
|
|
29
|
-
be verified from the loaded references or inspected project, prefix it with
|
|
30
|
-
`FROM_MEMORY (unverified):` and do not present it as a settled project fact.
|
|
29
|
+
Support recommendations with `[source: references/<file>.md]`. If a claim cannot be verified from the loaded references or inspected project, prefix it with `FROM_MEMORY (unverified):` and do not present it as a settled project fact.
|
|
31
30
|
|
|
32
31
|
## Project detection
|
|
33
32
|
|
|
@@ -105,11 +104,7 @@ Detailed examples and full implementation samples are available in: [Architectur
|
|
|
105
104
|
|
|
106
105
|
## Titanium style sheets rules (low freedom)
|
|
107
106
|
|
|
108
|
-
Before styling, detect `purgetss/` or `purgetss/config.cjs`. If present, invoke
|
|
109
|
-
the `purgetss` skill, verify every utility, and never edit the generated
|
|
110
|
-
`app/styles/app.tss`. Without PurgeTSS, use standard Alloy TSS files. In either
|
|
111
|
-
case, keep behavior in XML/controllers and use semantic colors for app-owned
|
|
112
|
-
surfaces when the project supports them.
|
|
107
|
+
Before styling, detect `purgetss/` or `purgetss/config.cjs`. If present, invoke the `purgetss` skill, verify every utility, and never edit the generated `app/styles/app.tss`. Without PurgeTSS, use standard Alloy TSS files. In either case, keep behavior in XML/controllers and use semantic colors for app-owned surfaces when the project supports them.
|
|
113
108
|
|
|
114
109
|
> **🚨 Critical: platform-specific properties require modifiers**
|
|
115
110
|
> Using `Ti.UI.iOS.*` or `Ti.UI.Android.*` properties without platform modifiers breaks cross-platform builds.
|
|
@@ -185,6 +180,8 @@ For the complete reference with examples, see [Alloy builtins and globals](refer
|
|
|
185
180
|
- [Alloy Builtins & Globals](references/alloy-builtins.md)
|
|
186
181
|
- [Code Conventions](references/code-conventions.md)
|
|
187
182
|
- [Controller Patterns](references/controller-patterns.md)
|
|
183
|
+
- [File Type Association](references/file-type-association.md) (custom extensions and UTIs, `CFBundleDocumentTypes`, Android intent filters, receiving files from AirDrop/Quick Share, document icons)
|
|
184
|
+
- [Sharing Content Out](references/sharing.md) (share sheet and intent chooser, why iOS still needs `dk.napp.social` and Android does not, text versus file, iPad popovers)
|
|
188
185
|
- [Feedback Surfaces & App-Owned UI](references/feedback-surfaces.md)
|
|
189
186
|
- [Feedback Widget Contracts](references/feedback-widget-contracts.md)
|
|
190
187
|
- [Feedback Migration](references/feedback-migration.md)
|
|
@@ -35,9 +35,7 @@ What this means: portrait-only apps will be forced into landscape/resized on lar
|
|
|
35
35
|
|
|
36
36
|
### It already started in Android 16 (API 36)
|
|
37
37
|
|
|
38
|
-
This is usually described as an API 37 change, but it lands one release earlier: an app
|
|
39
|
-
targeting **API 36** already has `android:screenOrientation` ignored on >= 600dp displays
|
|
40
|
-
running Android 16. The difference is that API 36 still ships an opt-out:
|
|
38
|
+
This is usually described as an API 37 change, but it lands one release earlier: an app targeting **API 36** already has `android:screenOrientation` ignored on >= 600dp displays running Android 16. The difference is that API 36 still ships an opt-out:
|
|
41
39
|
|
|
42
40
|
```xml
|
|
43
41
|
<!-- inside <application>, or on an individual <activity> -->
|
|
@@ -46,12 +44,9 @@ running Android 16. The difference is that API 36 still ships an opt-out:
|
|
|
46
44
|
android:value="true"/>
|
|
47
45
|
```
|
|
48
46
|
|
|
49
|
-
The opt-out stops working the moment you target API 37, so treat it as a bridge while the
|
|
50
|
-
layout is made adaptive — not as a fix. Source: [Android 16 behavior changes](https://developer.android.com/about/versions/16/behavior-changes-16).
|
|
47
|
+
The opt-out stops working the moment you target API 37, so treat it as a bridge while the layout is made adaptive — not as a fix. Source: [Android 16 behavior changes](https://developer.android.com/about/versions/16/behavior-changes-16).
|
|
51
48
|
|
|
52
|
-
Worth knowing when debugging: this only kicks in at >= 600dp. If a **phone** is rotating
|
|
53
|
-
despite a portrait lock, the cause is in the app, not in Android — see the orientation
|
|
54
|
-
debugging recipe in section 7.
|
|
49
|
+
Worth knowing when debugging: this only kicks in at >= 600dp. If a **phone** is rotating despite a portrait lock, the cause is in the app, not in Android — see the orientation debugging recipe in section 7.
|
|
55
50
|
|
|
56
51
|
### Related: edge-to-edge (API 36)
|
|
57
52
|
|
|
@@ -429,9 +424,7 @@ To verify the exact generated activity name, check `build/android/app/src/main/A
|
|
|
429
424
|
|
|
430
425
|
### Which activities a lock has to cover
|
|
431
426
|
|
|
432
|
-
`android:screenOrientation` is per-activity — it is not inherited from `<application>`. Every
|
|
433
|
-
window the app opens runs inside one of a small, fixed set of activities declared by the
|
|
434
|
-
Titanium AAR, so read the canonical list from the SDK instead of memorising it:
|
|
427
|
+
`android:screenOrientation` is per-activity — it is not inherited from `<application>`. Every window the app opens runs inside one of a small, fixed set of activities declared by the Titanium AAR, so read the canonical list from the SDK instead of memorising it:
|
|
435
428
|
|
|
436
429
|
```bash
|
|
437
430
|
cat ~/.gradle/caches/*/transforms/*/transformed/jetified-titanium-*/AndroidManifest.xml
|
|
@@ -448,8 +441,7 @@ ti.modules.titanium.media.TiVideoActivity
|
|
|
448
441
|
ti.modules.titanium.ui.android.TiPreferencesActivity
|
|
449
442
|
```
|
|
450
443
|
|
|
451
|
-
The seventh is the app's own root activity, generated from the `<name>` in tiapp.xml (see the
|
|
452
|
-
derivation rule above). Which one hosts what:
|
|
444
|
+
The seventh is the app's own root activity, generated from the `<name>` in tiapp.xml (see the derivation rule above). Which one hosts what:
|
|
453
445
|
|
|
454
446
|
| What the app does | Activity |
|
|
455
447
|
|---|---|
|
|
@@ -461,23 +453,12 @@ derivation rule above). Which one hosts what:
|
|
|
461
453
|
|
|
462
454
|
Two things are better left undeclared:
|
|
463
455
|
|
|
464
|
-
- **`android:configChanges`.** The CLI strips it before the merge even happens — `_build.js`
|
|
465
|
-
|
|
466
|
-
`removeActivityAttribute(name, 'android:configChanges')` on each, with the reason spelled out
|
|
467
|
-
in the source: *"Most devs don't set this right, causing UI to disappear when a config change
|
|
468
|
-
occurs for a missing setting."* So a hand-written subset is not dangerous, just dead weight —
|
|
469
|
-
Titanium's full list always wins.
|
|
470
|
-
- **A lock on `TiTranslucentActivity`.** Android 8–11 throws `IllegalStateException` ("Only
|
|
471
|
-
fullscreen opaque activities can request orientation") when a translucent activity requests a
|
|
472
|
-
fixed orientation. Declaring `orientationModes` on those windows is safer: the SDK applies it
|
|
473
|
-
inside a try/catch (`TiWindowProxy.setOrientationModes`), so the worst case is a modal that
|
|
474
|
-
rotates instead of an app that crashes.
|
|
456
|
+
- **`android:configChanges`.** The CLI strips it before the merge even happens — `_build.js` keeps a list of the `TiBaseActivity`-derived activities and calls `removeActivityAttribute(name, 'android:configChanges')` on each, with the reason spelled out in the source: *"Most devs don't set this right, causing UI to disappear when a config change occurs for a missing setting."* So a hand-written subset is not dangerous, just dead weight — Titanium's full list always wins.
|
|
457
|
+
- **A lock on `TiTranslucentActivity`.** Android 8–11 throws `IllegalStateException` ("Only fullscreen opaque activities can request orientation") when a translucent activity requests a fixed orientation. Declaring `orientationModes` on those windows is safer: the SDK applies it inside a try/catch (`TiWindowProxy.setOrientationModes`), so the worst case is a modal that rotates instead of an app that crashes.
|
|
475
458
|
|
|
476
459
|
### Prefer orientationModes over the manifest
|
|
477
460
|
|
|
478
|
-
`orientationModes` is defined on `TiWindowProxy`, so the same declaration works on `Window`,
|
|
479
|
-
`TabGroup` and `NavigationWindow`. Being per-window it also composes with form-factor queries,
|
|
480
|
-
which is what you usually want:
|
|
461
|
+
`orientationModes` is defined on `TiWindowProxy`, so the same declaration works on `Window`, `TabGroup` and `NavigationWindow`. Being per-window it also composes with form-factor queries, which is what you usually want:
|
|
481
462
|
|
|
482
463
|
```xml
|
|
483
464
|
<!-- PurgeTSS -->
|
|
@@ -489,14 +470,11 @@ which is what you usually want:
|
|
|
489
470
|
'#tabs[formFactor=handheld]': { orientationModes: [ Ti.UI.PORTRAIT ] }
|
|
490
471
|
```
|
|
491
472
|
|
|
492
|
-
Alloy resolves `formFactor` from `Ti.Platform.Android.physicalSizeCategory`, which is why this
|
|
493
|
-
is more reliable than any width comparison you could write yourself.
|
|
473
|
+
Alloy resolves `formFactor` from `Ti.Platform.Android.physicalSizeCategory`, which is why this is more reliable than any width comparison you could write yourself.
|
|
494
474
|
|
|
495
|
-
Keep the manifest for what Alloy cannot reach: the root activity, which shows the splash before
|
|
496
|
-
any JS runs, and optionally `TiActivity` as a blanket fallback for windows that carry no class.
|
|
475
|
+
Keep the manifest for what Alloy cannot reach: the root activity, which shows the splash before any JS runs, and optionally `TiActivity` as a blanket fallback for windows that carry no class.
|
|
497
476
|
|
|
498
|
-
A single Ti Element rule covers every window in the app without touching a single view, which
|
|
499
|
-
is usually all an Alloy app needs:
|
|
477
|
+
A single Ti Element rule covers every window in the app without touching a single view, which is usually all an Alloy app needs:
|
|
500
478
|
|
|
501
479
|
```javascript
|
|
502
480
|
// purgetss/config.cjs
|
|
@@ -511,8 +489,7 @@ module.exports = {
|
|
|
511
489
|
'Window[formFactor=handheld]': { orientationModes: [ Ti.UI.PORTRAIT ] }
|
|
512
490
|
```
|
|
513
491
|
|
|
514
|
-
Note it matches `Window` only, so a `TabGroup` still needs its own class. And because the
|
|
515
|
-
declaration is per-window, it does nothing for the splash — that one stays in the manifest.
|
|
492
|
+
Note it matches `Window` only, so a `TabGroup` still needs its own class. And because the declaration is per-window, it does nothing for the splash — that one stays in the manifest.
|
|
516
493
|
|
|
517
494
|
### Debugging a rotation that shouldn't happen
|
|
518
495
|
|
|
@@ -522,13 +499,9 @@ Ask the system for the effective value instead of re-reading the manifest:
|
|
|
522
499
|
adb shell dumpsys activity activities | grep -E "topResumedActivity|requestedOrientation"
|
|
523
500
|
```
|
|
524
501
|
|
|
525
|
-
If `requestedOrientation` disagrees with what tiapp.xml declares, something overrode it at
|
|
526
|
-
runtime — grep the app for `requestedOrientation` and `orientationModes` before touching the
|
|
527
|
-
manifest again. Note that an *existing* `orientationModes` can be the bug rather than the fix:
|
|
528
|
-
an empty array unlocks rotation (see the anti-patterns above).
|
|
502
|
+
If `requestedOrientation` disagrees with what tiapp.xml declares, something overrode it at runtime — grep the app for `requestedOrientation` and `orientationModes` before touching the manifest again. Note that an *existing* `orientationModes` can be the bug rather than the fix: an empty array unlocks rotation (see the anti-patterns above).
|
|
529
503
|
|
|
530
|
-
Before blaming the OS, let the SDK tell you. Titanium logs a warning when Android drops the
|
|
531
|
-
request on a large screen, so this either hits or rules that cause out immediately:
|
|
504
|
+
Before blaming the OS, let the SDK tell you. Titanium logs a warning when Android drops the request on a large screen, so this either hits or rules that cause out immediately:
|
|
532
505
|
|
|
533
506
|
```bash
|
|
534
507
|
adb logcat | grep "Orientation request will be ignored"
|
|
@@ -536,8 +509,7 @@ adb logcat | grep "Orientation request will be ignored"
|
|
|
536
509
|
# Orientation request will be ignored on Android 16+.
|
|
537
510
|
```
|
|
538
511
|
|
|
539
|
-
And to see the size bucket the device actually reports — `normal` means a phone, so the >= 600dp
|
|
540
|
-
rule does not apply and the cause is in the app:
|
|
512
|
+
And to see the size bucket the device actually reports — `normal` means a phone, so the >= 600dp rule does not apply and the cause is in the app:
|
|
541
513
|
|
|
542
514
|
```bash
|
|
543
515
|
adb shell am get-config # e.g. ...-sw360dp-w360dp-h802dp-normal-...
|
|
@@ -97,10 +97,7 @@ Alloy.Collections.notifications = new Backbone.Collection()
|
|
|
97
97
|
// <View if="Alloy.Globals.isLoggedIn">
|
|
98
98
|
```
|
|
99
99
|
|
|
100
|
-
Do not use `Alloy.Globals` as the default dark-mode palette. Prefer Titanium
|
|
101
|
-
semantic colors; when PurgeTSS is detected, use its semantic color and Appearance
|
|
102
|
-
workflow. Reserve a global runtime palette for custom theme requirements that
|
|
103
|
-
semantic light/dark/system roles cannot model. See [Theming](theming.md).
|
|
100
|
+
Do not use `Alloy.Globals` as the default dark-mode palette. Prefer Titanium semantic colors; when PurgeTSS is detected, use its semantic color and Appearance workflow. Reserve a global runtime palette for custom theme requirements that semantic light/dark/system roles cannot model. See [Theming](theming.md).
|
|
104
101
|
|
|
105
102
|
### Globals vs services
|
|
106
103
|
|
|
@@ -188,8 +188,7 @@ api.getFrames()
|
|
|
188
188
|
## Controller rules
|
|
189
189
|
|
|
190
190
|
**DO:**
|
|
191
|
-
- Use verified PurgeTSS utilities when detected; otherwise define styles in
|
|
192
|
-
per-controller TSS and manually maintained global TSS.
|
|
191
|
+
- Use verified PurgeTSS utilities when detected; otherwise define styles in per-controller TSS and manually maintained global TSS.
|
|
193
192
|
- Orchestrate view and model/collection interactions.
|
|
194
193
|
- Handle UI events and delegate to services.
|
|
195
194
|
- Format data for display (simple cases).
|
|
@@ -246,10 +245,7 @@ See [ControllerAutoCleanup.js](../assets/ControllerAutoCleanup.js) for the compl
|
|
|
246
245
|
|
|
247
246
|
## Widget structure
|
|
248
247
|
|
|
249
|
-
Widgets are self-contained components with their own public API, state, assets,
|
|
250
|
-
and lifecycle. Choose one for a portable boundary, not after an arbitrary number
|
|
251
|
-
of usages. A first-use component with timers, modal state, and cleanup can merit
|
|
252
|
-
a Widget; repeated static styling does not.
|
|
248
|
+
Widgets are self-contained components with their own public API, state, assets, and lifecycle. Choose one for a portable boundary, not after an arbitrary number of usages. A first-use component with timers, modal state, and cleanup can merit a Widget; repeated static styling does not.
|
|
253
249
|
|
|
254
250
|
```
|
|
255
251
|
app/widgets/
|
|
@@ -280,8 +276,7 @@ app/widgets/
|
|
|
280
276
|
}
|
|
281
277
|
```
|
|
282
278
|
|
|
283
|
-
Register the Widget in the app's `app/config.json`; the dependency key must
|
|
284
|
-
match the manifest `id`:
|
|
279
|
+
Register the Widget in the app's `app/config.json`; the dependency key must match the manifest `id`:
|
|
285
280
|
|
|
286
281
|
```json
|
|
287
282
|
{
|
|
@@ -491,10 +486,7 @@ render()
|
|
|
491
486
|
| Could be extracted to another project | Has no independent package contract |
|
|
492
487
|
| Needs its own `widget.json` manifest | Remains lightweight and app-local |
|
|
493
488
|
|
|
494
|
-
Usage count is supporting evidence only. Also consider a screen-local XML state
|
|
495
|
-
for loading/empty/error content, a style class for appearance without behavior,
|
|
496
|
-
or native UI when the operating system owns the workflow. See
|
|
497
|
-
[Feedback Widget Contracts](feedback-widget-contracts.md).
|
|
489
|
+
Usage count is supporting evidence only. Also consider a screen-local XML state for loading/empty/error content, a style class for appearance without behavior, or native UI when the operating system owns the workflow. See [Feedback Widget Contracts](feedback-widget-contracts.md).
|
|
498
490
|
|
|
499
491
|
## config.json reference
|
|
500
492
|
|
|
@@ -1,46 +1,35 @@
|
|
|
1
1
|
# Titanium Alloy anti-patterns
|
|
2
2
|
|
|
3
3
|
## 1. Inline styling instead of TSS
|
|
4
|
-
**Symptom:** Using inline attributes like `backgroundColor="#fff"` directly in XML views.
|
|
5
|
-
**Problem:** Inline attributes make theme changes impossible and scatter visual definitions across views.
|
|
6
|
-
**Solution:** Define styles in TSS files using IDs or class selectors.
|
|
4
|
+
**Symptom:** Using inline attributes like `backgroundColor="#fff"` directly in XML views. **Problem:** Inline attributes make theme changes impossible and scatter visual definitions across views. **Solution:** Define styles in TSS files using IDs or class selectors.
|
|
7
5
|
|
|
8
6
|
## 2. Fat controllers
|
|
9
|
-
**Symptom:** Controllers with 100+ lines handling logic, API, and UI.
|
|
10
|
-
**Solution:** Delegate business logic to `lib/services/` and API calls to `lib/api/`.
|
|
7
|
+
**Symptom:** Controllers with 100+ lines handling logic, API, and UI. **Solution:** Delegate business logic to `lib/services/` and API calls to `lib/api/`.
|
|
11
8
|
|
|
12
9
|
## 3. Memory leaks (Missing Cleanup)
|
|
13
|
-
**Symptom:** Adding `Ti.App` or `Alloy.Collections` listeners without a `cleanup()` function.
|
|
14
|
-
**Solution:** Always implement `$.cleanup = cleanup` and remove listeners there.
|
|
10
|
+
**Symptom:** Adding `Ti.App` or `Alloy.Collections` listeners without a `cleanup()` function. **Solution:** Always implement `$.cleanup = cleanup` and remove listeners there.
|
|
15
11
|
|
|
16
12
|
## 4. Direct native module calls
|
|
17
|
-
**Symptom:** Calling `require('ti.module')` directly in a controller.
|
|
18
|
-
**Solution:** Wrap it in a service in `lib/services/` (e.g., `audioService.js`).
|
|
13
|
+
**Symptom:** Calling `require('ti.module')` directly in a controller. **Solution:** Wrap it in a service in `lib/services/` (e.g., `audioService.js`).
|
|
19
14
|
|
|
20
15
|
## 5. Direct controller navigation
|
|
21
|
-
**Symptom:** `Alloy.createController('name').getView().open()`.
|
|
22
|
-
**Solution:** Use a Navigation Service to centralize `open/close` and trigger the `cleanup()` function automatically.
|
|
16
|
+
**Symptom:** `Alloy.createController('name').getView().open()`. **Solution:** Use a Navigation Service to centralize `open/close` and trigger the `cleanup()` function automatically.
|
|
23
17
|
|
|
24
18
|
## 6. Complex matrix animations
|
|
25
|
-
**Symptom:** Manual use of `Ti.UI.create2DMatrix()` for simple animations.
|
|
26
|
-
**Solution:** Use `Ti.UI.createAnimation()` for opacity, transform, and duration-based animations. Reserve 2D Matrix for complex multi-property transforms.
|
|
19
|
+
**Symptom:** Manual use of `Ti.UI.create2DMatrix()` for simple animations. **Solution:** Use `Ti.UI.createAnimation()` for opacity, transform, and duration-based animations. Reserve 2D Matrix for complex multi-property transforms.
|
|
27
20
|
|
|
28
21
|
## 7. Hardcoded strings & missing a11y
|
|
29
|
-
**Symptom:** `text="Login"` instead of `text="L('login')"`, or missing `accessibilityLabel`.
|
|
30
|
-
**Solution:** Always use i18n and accessibility properties.
|
|
22
|
+
**Symptom:** `text="Login"` instead of `text="L('login')"`, or missing `accessibilityLabel`. **Solution:** Always use i18n and accessibility properties.
|
|
31
23
|
|
|
32
24
|
## 8. Logic in TSS
|
|
33
|
-
**Symptom:** Using conditionals or calculations inside TSS.
|
|
34
|
-
**Solution:** Keep styling declarative in TSS files.
|
|
25
|
+
**Symptom:** Using conditionals or calculations inside TSS. **Solution:** Keep styling declarative in TSS files.
|
|
35
26
|
|
|
36
27
|
---
|
|
37
28
|
|
|
38
29
|
## Titanium layout anti-patterns
|
|
39
30
|
|
|
40
31
|
## 9. Padding on container views
|
|
41
|
-
**Symptom:** Setting `padding` on View, Window, ScrollView, or TableView.
|
|
42
|
-
**Problem:** Base container elements don't support padding in Titanium.
|
|
43
|
-
**Solution:** Use margins on children instead:
|
|
32
|
+
**Symptom:** Setting `padding` on View, Window, ScrollView, or TableView. **Problem:** Base container elements don't support padding in Titanium. **Solution:** Use margins on children instead:
|
|
44
33
|
```tss
|
|
45
34
|
/* WRONG */
|
|
46
35
|
"#container": { padding: 16 }
|
|
@@ -50,39 +39,21 @@
|
|
|
50
39
|
```
|
|
51
40
|
|
|
52
41
|
## 10. Redundant composite layout
|
|
53
|
-
**Symptom:** Adding `layout: 'composite'` to Views.
|
|
54
|
-
**Problem:** Composite (absolute positioning) is the DEFAULT layout. Setting it is redundant.
|
|
55
|
-
**Solution:** Omit `layout` for composite, only specify `layout: 'horizontal'` or `layout: 'vertical'` when needed.
|
|
42
|
+
**Symptom:** Adding `layout: 'composite'` to Views. **Problem:** Composite (absolute positioning) is the DEFAULT layout. Setting it is redundant. **Solution:** Omit `layout` for composite, only specify `layout: 'horizontal'` or `layout: 'vertical'` when needed.
|
|
56
43
|
|
|
57
44
|
## 11. Using `lib/` prefix in require statements
|
|
58
|
-
**Symptom:** `const service = require('lib/services/picsum')`
|
|
59
|
-
**Problem:** Alloy flattens the `lib/` folder during build. Files in `app/lib/services/` become `Resources/iphone/services/`.
|
|
60
|
-
**Solution:** Omit the `lib/` prefix: `const service = require('services/picsum')`
|
|
45
|
+
**Symptom:** `const service = require('lib/services/picsum')` **Problem:** Alloy flattens the `lib/` folder during build. Files in `app/lib/services/` become `Resources/iphone/services/`. **Solution:** Omit the `lib/` prefix: `const service = require('services/picsum')`
|
|
61
46
|
|
|
62
47
|
## 12. Wrong window ID in controller
|
|
63
|
-
**Symptom:** Using `$.index.open()` when the Window has `id="mainWindow"`.
|
|
64
|
-
**Problem:** Alloy generates `$` references from XML IDs. If Window is `id="mainWindow"`, `$.index` doesn't exist.
|
|
65
|
-
**Solution:** Match the ID: `$.mainWindow.open()`
|
|
48
|
+
**Symptom:** Using `$.index.open()` when the Window has `id="mainWindow"`. **Problem:** Alloy generates `$` references from XML IDs. If Window is `id="mainWindow"`, `$.index` doesn't exist. **Solution:** Match the ID: `$.mainWindow.open()`
|
|
66
49
|
|
|
67
50
|
## 13. Using `Ti.UI.createNotification`
|
|
68
|
-
**Symptom:** `Ti.UI.createNotification({ message: 'Hi' }).show()`
|
|
69
|
-
**Problem:** This API doesn't exist in Titanium. Causes "invalid method" error.
|
|
70
|
-
**Solution:** Classify the feedback before choosing UI. Use the app's Snackbar
|
|
71
|
-
Widget for transient foreground feedback, inline/Banner state for persistent
|
|
72
|
-
conditions, and an app-owned Dialog only for blocking or critical information.
|
|
73
|
-
Use the supported platform notification APIs only for actual OS notifications.
|
|
74
|
-
See [Feedback Surfaces](feedback-surfaces.md).
|
|
51
|
+
**Symptom:** `Ti.UI.createNotification({ message: 'Hi' }).show()` **Problem:** This API doesn't exist in Titanium. Causes "invalid method" error. **Solution:** Classify the feedback before choosing UI. Use the app's Snackbar Widget for transient foreground feedback, inline/Banner state for persistent conditions, and an app-owned Dialog only for blocking or critical information. Use the supported platform notification APIs only for actual OS notifications. See [Feedback Surfaces](feedback-surfaces.md).
|
|
75
52
|
|
|
76
53
|
## 14. Using nonexistent iOS share APIs
|
|
77
|
-
**Symptom:** `Ti.UI.iOS.createActivityPopover` or `alloy/social` with wrong methods.
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
- Keep sharing system-owned. Use the currently supported Titanium share/document
|
|
81
|
-
API or native module for the target platform and verify its current contract
|
|
82
|
-
with the `ti-api` skill.
|
|
83
|
-
- Do not imitate a share sheet with an app-owned `OptionDialog`. A Bottom Sheet
|
|
84
|
-
may choose an app action such as an export format, but the OS share surface
|
|
85
|
-
owns the final destination.
|
|
54
|
+
**Symptom:** `Ti.UI.iOS.createActivityPopover` or `alloy/social` with wrong methods. **Problem:** These APIs either don't exist or have changed. Causes runtime errors. **Solution:**
|
|
55
|
+
- Keep sharing system-owned. Use the currently supported Titanium share/document API or native module for the target platform and verify its current contract with the `ti-api` skill.
|
|
56
|
+
- Do not imitate a share sheet with an app-owned `OptionDialog`. A Bottom Sheet may choose an app action such as an export format, but the OS share surface owns the final destination.
|
|
86
57
|
|
|
87
58
|
## Community-Discovered Patterns
|
|
88
59
|
|
|
@@ -54,15 +54,11 @@ const name = user.name
|
|
|
54
54
|
|
|
55
55
|
## Styling with TSS
|
|
56
56
|
|
|
57
|
-
First detect `purgetss/` or `purgetss/config.cjs`. If present, invoke the
|
|
58
|
-
`purgetss` skill, use verified utilities, and do not edit generated
|
|
59
|
-
`app/styles/app.tss`. The manual TSS organization below applies when PurgeTSS is
|
|
60
|
-
not present or to specific properties for which that skill confirms no utility.
|
|
57
|
+
First detect `purgetss/` or `purgetss/config.cjs`. If present, invoke the `purgetss` skill, use verified utilities, and do not edit generated `app/styles/app.tss`. The manual TSS organization below applies when PurgeTSS is not present or to specific properties for which that skill confirms no utility.
|
|
61
58
|
|
|
62
59
|
### TSS file organization
|
|
63
60
|
|
|
64
|
-
Every controller can have a matching TSS file. Standard Alloy also supports a
|
|
65
|
-
manually maintained global `app.tss`.
|
|
61
|
+
Every controller can have a matching TSS file. Standard Alloy also supports a manually maintained global `app.tss`.
|
|
66
62
|
|
|
67
63
|
```
|
|
68
64
|
app/styles/
|
|
@@ -281,9 +281,7 @@ async function loadData() {
|
|
|
281
281
|
|
|
282
282
|
## Modal and dialog patterns
|
|
283
283
|
|
|
284
|
-
Declare the app-owned `dialog` and `bottomSheet` Widgets in the window XML.
|
|
285
|
-
Attach them after the host exists and destroy them from the controller cleanup.
|
|
286
|
-
Keep a modal Window for a distinct multi-step task, not for ordinary feedback.
|
|
284
|
+
Declare the app-owned `dialog` and `bottomSheet` Widgets in the window XML. Attach them after the host exists and destroy them from the controller cleanup. Keep a modal Window for a distinct multi-step task, not for ordinary feedback.
|
|
287
285
|
|
|
288
286
|
```javascript
|
|
289
287
|
function attachFeedback(hostWindow) {
|
|
@@ -20,16 +20,9 @@
|
|
|
20
20
|
|
|
21
21
|
## Reading these examples
|
|
22
22
|
|
|
23
|
-
Snippets that call `$.dialog`, `$.bottomSheet`, or `$.snackbar` expect the app to
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
[Feedback Surfaces](feedback-surfaces.md) covers which surface a given event
|
|
27
|
-
deserves. The `<Widget>` declaration alone will not compile until those Widgets
|
|
28
|
-
exist and are registered in `app/config.json`.
|
|
29
|
-
|
|
30
|
-
Styles below carry literal hex values so each example reads on its own. Production
|
|
31
|
-
code should name semantic color roles instead, so light and dark resolve without a
|
|
32
|
-
second code path — see [Theming and dark mode](theming.md).
|
|
23
|
+
Snippets that call `$.dialog`, `$.bottomSheet`, or `$.snackbar` expect the app to own those surfaces as Alloy Widgets under `app/widgets/`. Their public API, queueing, and cleanup rules live in [Feedback Widget Contracts](feedback-widget-contracts.md); [Feedback Surfaces](feedback-surfaces.md) covers which surface a given event deserves. The `<Widget>` declaration alone will not compile until those Widgets exist and are registered in `app/config.json`.
|
|
24
|
+
|
|
25
|
+
Styles below carry literal hex values so each example reads on its own. Production code should name semantic color roles instead, so light and dark resolve without a second code path — see [Theming and dark mode](theming.md).
|
|
33
26
|
|
|
34
27
|
## API client service
|
|
35
28
|
Standard logic for network requests.
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# Migrating feedback surfaces
|
|
2
2
|
|
|
3
|
-
Migrate semantics, not proxy names. Replacing every native dialog with the same
|
|
4
|
-
custom modal preserves the original design problem.
|
|
3
|
+
Migrate semantics, not proxy names. Replacing every native dialog with the same custom modal preserves the original design problem.
|
|
5
4
|
|
|
6
5
|
## Contents
|
|
7
6
|
|
|
@@ -21,9 +20,7 @@ Search the application for:
|
|
|
21
20
|
rg -n "createAlertDialog|createOptionDialog|createNotification|Toast|toast|snackbar|overlay" app/
|
|
22
21
|
```
|
|
23
22
|
|
|
24
|
-
Also inspect helpers that wrap these calls, XML overlays, controller timers,
|
|
25
|
-
global events, and callbacks that can present UI indirectly. Record for every
|
|
26
|
-
occurrence:
|
|
23
|
+
Also inspect helpers that wrap these calls, XML overlays, controller timers, global events, and callbacks that can present UI indirectly. Record for every occurrence:
|
|
27
24
|
|
|
28
25
|
- trigger and current message;
|
|
29
26
|
- success, information, warning, error, confirmation, or choice;
|
|
@@ -33,8 +30,7 @@ occurrence:
|
|
|
33
30
|
- callback side effects and cancellation behavior;
|
|
34
31
|
- owning window and cleanup path.
|
|
35
32
|
|
|
36
|
-
Do not implement the replacement until every occurrence has a semantic target
|
|
37
|
-
from `feedback-surfaces.md`.
|
|
33
|
+
Do not implement the replacement until every occurrence has a semantic target from `feedback-surfaces.md`.
|
|
38
34
|
|
|
39
35
|
## Classify before replacing
|
|
40
36
|
|
|
@@ -47,13 +43,9 @@ Use these defaults:
|
|
|
47
43
|
- uncommon irreversible decision → destructive Dialog;
|
|
48
44
|
- several related actions or app-data choices → Bottom Sheet;
|
|
49
45
|
- long structured content → scrollable sheet or dedicated screen;
|
|
50
|
-
- OS permission, picker, share, date/time, biometric, or purchase flow → keep the
|
|
51
|
-
system surface.
|
|
46
|
+
- OS permission, picker, share, date/time, biometric, or purchase flow → keep the system surface.
|
|
52
47
|
|
|
53
|
-
Preserve domain behavior separately from presentation. Services return results
|
|
54
|
-
or errors; the window/controller that owns visible context chooses the surface.
|
|
55
|
-
Do not let a service create app-owned UI, especially before the root window
|
|
56
|
-
exists.
|
|
48
|
+
Preserve domain behavior separately from presentation. Services return results or errors; the window/controller that owns visible context chooses the surface. Do not let a service create app-owned UI, especially before the root window exists.
|
|
57
49
|
|
|
58
50
|
## Migrate one window at a time
|
|
59
51
|
|
|
@@ -62,19 +54,15 @@ exists.
|
|
|
62
54
|
3. Attach modal Widgets after the window exists.
|
|
63
55
|
4. Route each old result to its classified surface.
|
|
64
56
|
5. Call Widget `destroy()` from the window close cleanup.
|
|
65
|
-
6. Exercise success, failure, cancel, Back, backdrop, double tap, and consecutive
|
|
66
|
-
|
|
67
|
-
7. Remove the old dialog, local overlay, helper, timer, and listener after the
|
|
68
|
-
migrated window passes verification.
|
|
57
|
+
6. Exercise success, failure, cancel, Back, backdrop, double tap, and consecutive presentation paths.
|
|
58
|
+
7. Remove the old dialog, local overlay, helper, timer, and listener after the migrated window passes verification.
|
|
69
59
|
8. Commit the completed vertical slice before moving to the next window.
|
|
70
60
|
|
|
71
|
-
Do not keep commented legacy implementations. Version control is the rollback
|
|
72
|
-
mechanism; commented duplicates hide incomplete migrations and stale callbacks.
|
|
61
|
+
Do not keep commented legacy implementations. Version control is the rollback mechanism; commented duplicates hide incomplete migrations and stale callbacks.
|
|
73
62
|
|
|
74
63
|
## Deliberate silence
|
|
75
64
|
|
|
76
|
-
Cancellation or an unknowable result can be a complete, intentional outcome.
|
|
77
|
-
Model it explicitly and do not infer a message from falsy values.
|
|
65
|
+
Cancellation or an unknowable result can be a complete, intentional outcome. Model it explicitly and do not infer a message from falsy values.
|
|
78
66
|
|
|
79
67
|
Example contract for an exported file:
|
|
80
68
|
|
|
@@ -103,9 +91,7 @@ function handleSaveResult(error, result) {
|
|
|
103
91
|
}
|
|
104
92
|
```
|
|
105
93
|
|
|
106
|
-
Do not add "Cancelled" for `saved === false`. Do not claim success for
|
|
107
|
-
`saved === null`. Cover both branches with tests so a future fallback does not
|
|
108
|
-
erase the intended silence.
|
|
94
|
+
Do not add "Cancelled" for `saved === false`. Do not claim success for `saved === null`. Cover both branches with tests so a future fallback does not erase the intended silence.
|
|
109
95
|
|
|
110
96
|
## Contract tests
|
|
111
97
|
|
|
@@ -114,8 +100,7 @@ Add repository-appropriate static or unit tests for:
|
|
|
114
100
|
- all Widget folders, manifests, XML views, controllers, and dependency entries;
|
|
115
101
|
- each public method documented in `feedback-widget-contracts.md`;
|
|
116
102
|
- the expected Widget declared in every migrated window;
|
|
117
|
-
- absence of app-owned `Ti.UI.createAlertDialog` and
|
|
118
|
-
`Ti.UI.createOptionDialog` defaults;
|
|
103
|
+
- absence of app-owned `Ti.UI.createAlertDialog` and `Ti.UI.createOptionDialog` defaults;
|
|
119
104
|
- removal of the previous local Snackbar/overlay and timer;
|
|
120
105
|
- Snackbar replacement and timer reset;
|
|
121
106
|
- Dialog and Bottom Sheet FIFO ordering;
|
|
@@ -124,18 +109,15 @@ Add repository-appropriate static or unit tests for:
|
|
|
124
109
|
- cleanup of active and queued presentations;
|
|
125
110
|
- explicit silence for cancel and indeterminate save results.
|
|
126
111
|
|
|
127
|
-
Do not enforce blanket bans on native system surfaces. A remaining native call
|
|
128
|
-
must be tied to an OS-owned interaction or documented fallback.
|
|
112
|
+
Do not enforce blanket bans on native system surfaces. A remaining native call must be tied to an OS-owned interaction or documented fallback.
|
|
129
113
|
|
|
130
114
|
## Delivery verification
|
|
131
115
|
|
|
132
|
-
Run the project's real commands rather than assuming names. A complete migration
|
|
133
|
-
normally includes:
|
|
116
|
+
Run the project's real commands rather than assuming names. A complete migration normally includes:
|
|
134
117
|
|
|
135
118
|
1. lint with zero errors and no new warnings;
|
|
136
119
|
2. unit and contract tests with no new failures;
|
|
137
|
-
3. PurgeTSS generation when detected, followed by inspection of unused or
|
|
138
|
-
unsupported classes at the end of `app.tss`;
|
|
120
|
+
3. PurgeTSS generation when detected, followed by inspection of unused or unsupported classes at the end of `app.tss`;
|
|
139
121
|
4. Alloy compilation for Android and iOS;
|
|
140
122
|
5. native builds for every supported platform;
|
|
141
123
|
6. manual checks in light, dark, and system appearance;
|
|
@@ -144,9 +126,7 @@ normally includes:
|
|
|
144
126
|
9. VoiceOver and TalkBack focus/traversal checks;
|
|
145
127
|
10. consecutive presentation and window-close cleanup checks.
|
|
146
128
|
|
|
147
|
-
Record pre-existing failures before migration and require the final run to add
|
|
148
|
-
none. Build artifacts are not proof of usable modal behavior; perform the manual
|
|
149
|
-
interaction checks on device or simulator.
|
|
129
|
+
Record pre-existing failures before migration and require the final run to add none. Build artifacts are not proof of usable modal behavior; perform the manual interaction checks on device or simulator.
|
|
150
130
|
|
|
151
131
|
## When to share Widgets between apps
|
|
152
132
|
|
|
@@ -157,9 +137,7 @@ Extract or copy a Widget into another app only when:
|
|
|
157
137
|
- appearance depends on a documented semantic-token contract;
|
|
158
138
|
- platform and minimum Titanium/Alloy versions are declared;
|
|
159
139
|
- lifecycle and accessibility tests travel with it;
|
|
160
|
-
- at least two apps need the same behavior, or one app has a clear portability
|
|
161
|
-
requirement.
|
|
140
|
+
- at least two apps need the same behavior, or one app has a clear portability requirement.
|
|
162
141
|
|
|
163
|
-
Do not extract merely because a view appeared three times. Extract because the
|
|
164
|
-
boundary is independently understandable, testable, and maintainable.
|
|
142
|
+
Do not extract merely because a view appeared three times. Extract because the boundary is independently understandable, testable, and maintainable.
|
|
165
143
|
|