@maccesar/titools 4.23.0 → 5.0.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 +1 -1
- package/package.json +1 -1
- package/skills/alloy-guides/references/MODELS.md +2 -2
- package/skills/alloy-guides/references/MODELS_ADVANCED.md +1 -1
- package/skills/alloy-guides/references/VIEWS_XML.md +13 -13
- package/skills/alloy-howtos/references/best_practices.md +2 -2
- package/skills/alloy-howtos/references/cli_reference.md +1 -1
- package/skills/alloy-howtos/references/debugging_troubleshooting.md +1 -1
- package/skills/purgetss/references/classic-projects.md +1 -1
- package/skills/ti-api/references/api-data-network.md +1 -1
- package/skills/ti-api/references/api-media.md +1 -1
- package/skills/ti-api/references/api-services.md +8 -5
- package/skills/ti-api/references/api-ui-lists.md +3 -2
- package/skills/ti-api/references/api-ui-text-input.md +1 -1
- package/skills/ti-expert/references/feedback-surfaces.md +1 -1
- package/skills/ti-expert/references/push-notifications.md +68 -4
- package/skills/ti-guides/references/resources.md +1 -1
- package/skills/ti-guides/references/sdk-release-notes.md +22 -11
- package/skills/ti-howtos/references/extending-titanium.md +77 -0
package/README.md
CHANGED
|
@@ -191,7 +191,7 @@ Notes:
|
|
|
191
191
|
- `ti-reuse-first` runs before the others. It asks whether a thing needs to exist and what already does it — a search order that stops at the first rung that holds — and sweeps an existing `app/` for near-identical controllers, single-listener events, wrappers around one call, and layout recomputed in JavaScript. It is deliberately two-sided: it says when adding a module or a Widget is the right answer, so the bias does not simply invert.
|
|
192
192
|
- `ti-game` documents the `ti.game` native module (2D sprite engine on OpenGL ES 2.0, Android and iOS). Its API reference was verified against the module source and tracks upstream `main` (the module ships features ahead of its manifest version); it applies to Alloy and Classic projects alike and does not depend on PurgeTSS.
|
|
193
193
|
- `ti-synthengine` translates aesthetic sound requests into strict, production-ready JavaScript for the cross-platform `ti.synthengine` 1.0.0 module. Seven references preserve the official documentation and an eighth adds curated retro, xylophone, memory-pad, polyphony and timer-ownership recipes; the API contract was cross-checked against both native implementations.
|
|
194
|
-
- The latter five are documentation-mirror skills
|
|
194
|
+
- The latter five are documentation-mirror skills, audited via the internal `titools-skill-auditor` against three sources: `ti-api` against the API definitions in `tidev/titanium-sdk` (`apidoc/`, anchored to the newest GA tag), the guide skills against the rewritten guides in `tidev/titaniumsdk.com` (`content/docs/`), and both against the frozen `tidev/titanium-docs` corpus as an archive. The archive is additive: TiDev condensed 336 legacy pages into 62, and the material it left behind is kept here rather than deleted.
|
|
195
195
|
- `purgetss` reference files are audited against the official PurgeTSS documentation, but its workflow conventions are opinionated.
|
|
196
196
|
|
|
197
197
|
---
|
package/package.json
CHANGED
|
@@ -291,7 +291,7 @@ In Alloy, collection data can be synchronized to a view object, or a single mode
|
|
|
291
291
|
|
|
292
292
|
#### Collection-View Binding
|
|
293
293
|
|
|
294
|
-
To enable collection-view binding, create a global singleton or controller-specific collection using the [Collection tag](https://titaniumsdk.com/
|
|
294
|
+
To enable collection-view binding, create a global singleton or controller-specific collection using the [Collection tag](https://titaniumsdk.com/docs/alloy/views) in the XML markup of the main view, then add the view object you want to bind data to. The following Titanium view objects support binding to a Collection:
|
|
295
295
|
|
|
296
296
|
| View Object | Since Alloy version | Add data binding attributes to... | Repeater Object to map model attributes to view properties |
|
|
297
297
|
| -------------- | ------------------- | ---------------------------------------------- | ------------------------------------------------------------------------------ |
|
|
@@ -416,7 +416,7 @@ The application is now setup for basic collection-view binding. When any new dat
|
|
|
416
416
|
|
|
417
417
|
#### Model-View Binding
|
|
418
418
|
|
|
419
|
-
To bind a single model to a component, create a global singleton or controller-specific model using the [Model tag](https://titaniumsdk.com/
|
|
419
|
+
To bind a single model to a component, create a global singleton or controller-specific model using the [Model tag](https://titaniumsdk.com/docs/alloy/views) in the XML markup of the main view and map the model attribute to the view component. To map the attribute to the view component, prefix the model name or id to the attribute, then enclose it with curly brackets or braces ('{' and '}').
|
|
420
420
|
|
|
421
421
|
To do complex transformations on the model attributes, extend the model prototype with a `transform()` function. It should return the modified model as a JSON object.
|
|
422
422
|
|
|
@@ -33,7 +33,7 @@ In Alloy, a sync adapter allows you to store and load your models to a persisten
|
|
|
33
33
|
|
|
34
34
|
#### Backbone Sync
|
|
35
35
|
|
|
36
|
-
Backbone syncs your models to persistent storage devices based on the implementation of the [Backbone.sync method](https://titaniumsdk.com/
|
|
36
|
+
Backbone syncs your models to persistent storage devices based on the implementation of the [Backbone.sync method](https://titaniumsdk.com/docs/alloy/models). Since Backbone's primary use is for web applications, by default, the Backbone.sync method executes RESTful JSON requests to a URL specified by the Model.urlRoot or Collection.url attribute, when these classes are created.
|
|
37
37
|
|
|
38
38
|
Models are accessed from persistent storage based on the `id` attribute. To override this, set the `idAttribute` property of the model. The `cid` (client ID) is a special property of models that is automatically assigned when they are first created. Client IDs are useful when the model has not yet been saved to the server and does not have its real `id` yet.
|
|
39
39
|
|
|
@@ -584,16 +584,16 @@ If you have a collection of model data that needs to be automatically updated to
|
|
|
584
584
|
|
|
585
585
|
Some Titanium view elements use special syntax. Refer to the **Alloy XML Markup** examples in the Titanium API Guides site for the following view objects:
|
|
586
586
|
|
|
587
|
-
* [AlertDialog](https://titaniumsdk.com/
|
|
588
|
-
* [ButtonBar](https://titaniumsdk.com/
|
|
589
|
-
* [CoverFlowView](https://titaniumsdk.com/
|
|
590
|
-
* [DashboardView](https://titaniumsdk.com/
|
|
591
|
-
* [ListView](https://titaniumsdk.com/
|
|
592
|
-
* [Map](https://titaniumsdk.com/
|
|
593
|
-
* [Menu](https://titaniumsdk.com/
|
|
594
|
-
* [OptionBar](https://titaniumsdk.com/
|
|
595
|
-
* [OptionDialog](https://titaniumsdk.com/
|
|
596
|
-
* [Picker](https://titaniumsdk.com/
|
|
597
|
-
* [SplitWindow](https://titaniumsdk.com/
|
|
598
|
-
* [TabbedBar](https://titaniumsdk.com/
|
|
599
|
-
* [Toolbar](https://titaniumsdk.com/
|
|
587
|
+
* [AlertDialog](https://titaniumsdk.com/docs/sdk/Titanium.UI.AlertDialog)
|
|
588
|
+
* [ButtonBar](https://titaniumsdk.com/docs/sdk/Titanium.UI.ButtonBar)
|
|
589
|
+
* [CoverFlowView](https://titaniumsdk.com/docs/sdk/Titanium.UI.iOS.CoverFlowView)
|
|
590
|
+
* [DashboardView](https://titaniumsdk.com/docs/sdk/Titanium.UI.DashboardView)
|
|
591
|
+
* [ListView](https://titaniumsdk.com/docs/sdk/Titanium.UI.ListView)
|
|
592
|
+
* [Map](https://titaniumsdk.com/modules/ti.map)
|
|
593
|
+
* [Menu](https://titaniumsdk.com/docs/sdk/Titanium.Android.Menu)
|
|
594
|
+
* [OptionBar](https://titaniumsdk.com/docs/sdk/Titanium.UI.OptionBar)
|
|
595
|
+
* [OptionDialog](https://titaniumsdk.com/docs/sdk/Titanium.UI.OptionDialog)
|
|
596
|
+
* [Picker](https://titaniumsdk.com/docs/sdk/Titanium.UI.Picker)
|
|
597
|
+
* [SplitWindow](https://titaniumsdk.com/docs/sdk/Titanium.UI.iOS.SplitWindow)
|
|
598
|
+
* [TabbedBar](https://titaniumsdk.com/docs/sdk/Titanium.UI.iOS.TabbedBar)
|
|
599
|
+
* [Toolbar](https://titaniumsdk.com/docs/sdk/Titanium.UI.Toolbar)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Alloy best practices and recommendations
|
|
2
2
|
|
|
3
|
-
Recommendations for writing Alloy apps. This supplements the Titanium SDK [Best Practices and Recommendations](https://titaniumsdk.com/
|
|
3
|
+
Recommendations for writing Alloy apps. This supplements the Titanium SDK [Best Practices and Recommendations](https://titaniumsdk.com/docs/build) guide and focuses on coding style and conventions.
|
|
4
4
|
|
|
5
5
|
## Titanium-to-Alloy Guidance
|
|
6
6
|
|
|
@@ -29,7 +29,7 @@ Decide whether to adapt Alloy to your existing structure, or to adopt Alloy's st
|
|
|
29
29
|
### Naming Conventions
|
|
30
30
|
|
|
31
31
|
- **Do not use double underscore prefixes** on variables, properties, or function names (e.g., `__foo`). They are reserved for Alloy and may cause conflicts and unexpected behavior.
|
|
32
|
-
- **Do not use JavaScript reserved words as IDs.** See [Titanium SDK Reserved Words](https://titaniumsdk.com/
|
|
32
|
+
- **Do not use JavaScript reserved words as IDs.** See [Titanium SDK Reserved Words](https://titaniumsdk.com/docs/buildReserved_Words/) for the complete list.
|
|
33
33
|
|
|
34
34
|
### Global Variables
|
|
35
35
|
|
|
@@ -123,7 +123,7 @@ Compiler options reference the `event.alloyConfig` object in [Build Configuratio
|
|
|
123
123
|
|
|
124
124
|
### run
|
|
125
125
|
|
|
126
|
-
Use `titanium build` command to run Alloy projects. See [Titanium Command-Line Interface Reference](https://titaniumsdk.com/
|
|
126
|
+
Use `titanium build` command to run Alloy projects. See [Titanium Command-Line Interface Reference](https://titaniumsdk.com/docs/reference/cli).
|
|
127
127
|
|
|
128
128
|
> **💡 Common ti build pitfalls**
|
|
129
129
|
>
|
|
@@ -75,4 +75,4 @@ Search [existing issues](https://github.com/tidev/alloy/issues) first to avoid d
|
|
|
75
75
|
- Include Alloy version (`alloy --version`)
|
|
76
76
|
- Include environment information
|
|
77
77
|
|
|
78
|
-
For detailed instructions, see [How to Report a Bug or Make a Feature Request](https://titaniumsdk.com/
|
|
78
|
+
For detailed instructions, see [How to Report a Bug or Make a Feature Request](https://titaniumsdk.com/contribute).
|
|
@@ -64,7 +64,7 @@ Titanium resolves local CommonJS paths from `Resources/`. Omit both the `Resourc
|
|
|
64
64
|
| `Resources/lib/materialsymbols.js` | `require('lib/materialsymbols')` |
|
|
65
65
|
| `Resources/lib/framework7icons.js` | `require('lib/framework7icons')` |
|
|
66
66
|
|
|
67
|
-
This follows Titanium's [CommonJS module path resolution](https://titaniumsdk.com/
|
|
67
|
+
This follows Titanium's [CommonJS module path resolution](https://titaniumsdk.com/docs/build/modules).
|
|
68
68
|
|
|
69
69
|
## Deployment Targets
|
|
70
70
|
|
|
@@ -107,7 +107,7 @@ On the iOS platform, closing the database also closes the result set, that is, y
|
|
|
107
107
|
> Platforms: both
|
|
108
108
|
> Type: module
|
|
109
109
|
|
|
110
|
-
For examples of using the Filesystem APIs, refer to the [Filesystem Access and Storage guide](https://titaniumsdk.com/
|
|
110
|
+
For examples of using the Filesystem APIs, refer to the [Filesystem Access and Storage guide](https://titaniumsdk.com/docs/build/data) as well as the other Filesystem submodule API documentation.
|
|
111
111
|
|
|
112
112
|
### Properties (unique: 11/21)
|
|
113
113
|
| Property | Type | Default | Platform | Description |
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
> Platforms: both
|
|
22
22
|
> Type: module
|
|
23
23
|
|
|
24
|
-
The Media module is used to access the device's media-related functionality, such as using the device's camera and photo gallery, playing media files, or recording audio or video. For examples of using the Media APIs, refer to the [Working with Media APIs guide](https://titaniumsdk.com/
|
|
24
|
+
The Media module is used to access the device's media-related functionality, such as using the device's camera and photo gallery, playing media files, or recording audio or video. For examples of using the Media APIs, refer to the [Working with Media APIs guide](https://titaniumsdk.com/docs/build/media) in addition to the other media submodule API documentation. **Note**: Some third party Android camera apps may choose to ignore video recording quality settings. If you wish to specifically set the video quality, don't assume `EXTRA_VIDEO_QUALITY` intent will be respected by the camera app and use Titanium's built-in camera window which can be used to assign the `overlay` property when calling the `showCamera()` method.
|
|
25
25
|
|
|
26
26
|
### Properties (unique: 28/178)
|
|
27
27
|
| Property | Type | Default | Platform | Description |
|
|
@@ -108,15 +108,18 @@ to request runtime permissions.
|
|
|
108
108
|
|
|
109
109
|
The API supports retrieving information about the attendee of an event.
|
|
110
110
|
|
|
111
|
-
|
|
111
|
+
The type declares `platforms: [iphone, ipad, macos]`, but four of its properties declare Android as well. Read the per-property column, not the type header.
|
|
112
|
+
|
|
113
|
+
### Properties (unique: 7/9)
|
|
112
114
|
| Property | Type | Default | Platform | Description |
|
|
113
115
|
|----------|------|---------|----------|-------------|
|
|
114
116
|
| isOrganizer | Boolean | — | ios | Indicates whether this attendee is the event organizer. |
|
|
115
|
-
| name | String | — |
|
|
116
|
-
| email | String | — |
|
|
117
|
+
| name | String | — | both | The attendee name. |
|
|
118
|
+
| email | String | — | both | The attendee email. |
|
|
119
|
+
| relationship | Number | — | android | The relationship of the attendee to the user. Read-only. Values come from the `Ti.Calendar.RELATIONSHIP_*` constants. Since 6.2.0. |
|
|
117
120
|
| role | Number | — | ios | The role of the attendee. |
|
|
118
|
-
| type | Number | — |
|
|
119
|
-
| status | Number | — |
|
|
121
|
+
| type | Number | — | both | The type of the attendee. |
|
|
122
|
+
| status | Number | — | both | The status of the attendee. |
|
|
120
123
|
|
|
121
124
|
|
|
122
125
|
|
|
@@ -336,7 +336,7 @@ A `TableView` object is a container for [TableViewSection](Titanium.UI.TableView
|
|
|
336
336
|
|
|
337
337
|
Use the <Titanium.UI.createTableView> method or **`<TableView>`** Alloy element to create a `TableView`.
|
|
338
338
|
|
|
339
|
-
Also see the [TableViews guide](https://titaniumsdk.com/
|
|
339
|
+
Also see the [TableViews guide](https://titaniumsdk.com/docs/build/ui/lists).
|
|
340
340
|
|
|
341
341
|
### Creating Tables
|
|
342
342
|
|
|
@@ -564,12 +564,13 @@ Rows may be created using the properties directly available on the `TableViewRow
|
|
|
564
564
|
|
|
565
565
|
|
|
566
566
|
|
|
567
|
-
### Events (
|
|
567
|
+
### Events (5)
|
|
568
568
|
| Event | Platform | Description |
|
|
569
569
|
|-------|----------|-------------|
|
|
570
570
|
| click | both | Fired when a table row is clicked by the user. |
|
|
571
571
|
| touchcancel | ios | Fired when a touch gesture is interrupted by the device. |
|
|
572
572
|
| touchend | ios | Fired when a touch gesture is complete. |
|
|
573
|
+
| touchmove | ios | Fired as soon as the device detects movement of a touch. A row touch event and a table `scroll` event cannot occur concurrently: if the table begins to scroll during a touch, `touchcancel` fires before `scroll` begins. |
|
|
573
574
|
| touchstart | ios | Fired as soon as the device detects a touch gesture against this view. |
|
|
574
575
|
|
|
575
576
|
### Related Types
|
|
@@ -337,7 +337,7 @@ The AttributedString proxy is created with the <Titanium.UI.createAttributedStri
|
|
|
337
337
|
|
|
338
338
|
The `text` property must be set initially in the constructor when creating an attributed string. The [attributes](Titanium.UI.AttributedString.attributes) can either be set in the constructor or after it has been created.
|
|
339
339
|
|
|
340
|
-
For examples of Attributed Strings, see the [Attributed Strings guide](https://titaniumsdk.com/
|
|
340
|
+
For examples of Attributed Strings, see the [Attributed Strings guide](https://titaniumsdk.com/docs/build/ui).
|
|
341
341
|
|
|
342
342
|
### Properties (unique: 2/5)
|
|
343
343
|
| Property | Type | Default | Platform | Description |
|
|
@@ -102,7 +102,7 @@ Standardize buttons, cards, badges, chips, skeletons, and coach marks only when
|
|
|
102
102
|
|
|
103
103
|
## References
|
|
104
104
|
|
|
105
|
-
- [Titanium Alloy Widgets](https://titaniumsdk.com/
|
|
105
|
+
- [Titanium Alloy Widgets](https://titaniumsdk.com/docs/alloy/widgets)
|
|
106
106
|
- [Apple Human Interface Guidelines: Alerts](https://developer.apple.com/design/human-interface-guidelines/alerts)
|
|
107
107
|
- [Apple Human Interface Guidelines: Modality](https://developer.apple.com/design/human-interface-guidelines/modality)
|
|
108
108
|
- [Material Design 3: Snackbar](https://m3.material.io/components/snackbar/overview)
|
|
@@ -79,6 +79,26 @@ if (OS_ANDROID) {
|
|
|
79
79
|
|
|
80
80
|
**Wrap the listeners.** `Ti.App.addEventListener('resumed', mostrarAviso)` passes the event object as the first argument, and if the function's first parameter is an Intent, the code blows up inside `getStringExtra`. The same trap catches `Alloy.Events.on('algo', unaFuncionConParametros)`.
|
|
81
81
|
|
|
82
|
+
### Registering on Android: two calls that go quiet
|
|
83
|
+
|
|
84
|
+
The channel has to exist and be handed to the module **before** asking for the token, and its id has to match the `default_notification_channel_id` meta-data in `tiapp.xml`. Mismatch them and the notification is posted against a channel nobody declared.
|
|
85
|
+
|
|
86
|
+
```javascript
|
|
87
|
+
modulo.notificationChannel = Ti.Android.NotificationManager.createNotificationChannel({
|
|
88
|
+
id: 'default_channel', // the same id tiapp.xml declares
|
|
89
|
+
name: L('push_channel_name'),
|
|
90
|
+
importance: Ti.Android.IMPORTANCE_DEFAULT
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
modulo.registerForPushNotifications()
|
|
94
|
+
|
|
95
|
+
// Firebase does not announce a token it already had, so didRefreshRegistrationToken
|
|
96
|
+
// may never fire on a device that registered before. Ask for it once, a moment later.
|
|
97
|
+
setTimeout(() => guardarToken(modulo.fcmToken), 4000)
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
That last line is the one that bites. On a fresh install the event fires and everything looks fine; on a device that already had a token from an earlier run it never fires, the app never sends the token to the server, and every push to that device is addressed to nobody. Make `guardarToken` idempotent — it will be called more than once with the same value.
|
|
101
|
+
|
|
82
102
|
## 2. The payload decides how much of the module runs
|
|
83
103
|
|
|
84
104
|
Android distinguishes a *notification message* from a *data message*, and the difference is not cosmetic.
|
|
@@ -151,11 +171,34 @@ Everything above is what the published module needs. A module that fires `didOpe
|
|
|
151
171
|
Verified on a physical Android 15 phone and an API 35 emulator, each row confirmed by a log line naming the route. The first three ran back to back on one process id with a game in progress, which survived; only the fourth starts a new process.
|
|
152
172
|
|
|
153
173
|
```javascript
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
174
|
+
if (OS_ANDROID) {
|
|
175
|
+
// With the runtime alive the launcher Intent is not delivered a second time,
|
|
176
|
+
// so this event is the only way in.
|
|
177
|
+
modulo.addEventListener('didOpenNotification', (e) => {
|
|
178
|
+
abrirPanel((e.message && e.message.data) || {})
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
// On a cold start the module does not exist yet when the notification is
|
|
182
|
+
// opened, so the payload travels in the Intent. This is that route.
|
|
183
|
+
const desdeElIntent = (origen) => {
|
|
184
|
+
const contenido = Ti.Android.rootActivity.intent.getStringExtra('fcm_data')
|
|
185
|
+
|
|
186
|
+
if (!contenido) {
|
|
187
|
+
log.debug(`Nothing in the Intent (${origen})`)
|
|
188
|
+
return
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
modulo.clearLastData()
|
|
192
|
+
abrirPanel(JSON.parse(contenido))
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
Ti.App.addEventListener('resumed', () => desdeElIntent('resumed'))
|
|
196
|
+
desdeElIntent('arranque')
|
|
197
|
+
}
|
|
157
198
|
```
|
|
158
199
|
|
|
200
|
+
`resumed` stays even though the event covers the warm tap: it is what catches an Intent the module left behind on a version that still writes one. When the event delivered the payload the Intent is empty and the handler returns without opening anything, which is the exclusivity the section below describes.
|
|
201
|
+
|
|
159
202
|
The payload sits under `message.data`, the same place `didReceiveMessage` puts it on both its live and its cold-start path, so one accessor covers a tap and an arrival. The first version of the event wrapped it directly in `message`; if the module you have predates 3.6.0, read `e.message` instead, or log the event once and look.
|
|
160
203
|
|
|
161
204
|
The Intent is still how a cold start arrives, so keep that path. Check `timodule.xml` in your installed module for the event, or just add the listener and watch whether it fires: a module without it ignores the registration silently.
|
|
@@ -249,7 +292,15 @@ Verified case by case on an iPad. The third row is the one that does not work on
|
|
|
249
292
|
|
|
250
293
|
```javascript
|
|
251
294
|
Ti.Network.registerForPushNotifications({
|
|
252
|
-
success: (e) => {
|
|
295
|
+
success: (e) => {
|
|
296
|
+
// e.deviceToken is the APNs one. It is not what you send to a server that
|
|
297
|
+
// pushes through FCM, and Firebase picks it up on its own — see below.
|
|
298
|
+
log.info(`APNs token: ${e.deviceToken ? 'received' : 'EMPTY'}`)
|
|
299
|
+
|
|
300
|
+
modulo.fetchToken((r) => {
|
|
301
|
+
if (r && r.token) { guardarToken(r.token) }
|
|
302
|
+
})
|
|
303
|
+
},
|
|
253
304
|
error: (e) => { log.error(e.error) },
|
|
254
305
|
callback: (e) => {
|
|
255
306
|
// Foreground, background and closed all arrive here.
|
|
@@ -274,6 +325,16 @@ if (OS_IOS) {
|
|
|
274
325
|
}
|
|
275
326
|
```
|
|
276
327
|
|
|
328
|
+
**The token comes from `fetchToken`, not from the property.** Right after APNs answers, `modulo.fcmToken` is empty: Firebase still has to exchange the APNs token for its own, and that exchange has no fixed duration. Waiting does not fix it — four seconds was still empty on a device. `fetchToken` calls back when the token exists, which is the only way to know.
|
|
329
|
+
|
|
330
|
+
```javascript
|
|
331
|
+
// The apnsToken is never assigned by hand. Firebase's swizzling does it, and
|
|
332
|
+
// setting it yourself produces BadDeviceToken when you send.
|
|
333
|
+
modulo.fetchToken((e) => {
|
|
334
|
+
if (e && e.token) { guardarToken(e.token) }
|
|
335
|
+
})
|
|
336
|
+
```
|
|
337
|
+
|
|
277
338
|
**A data-only message shows nothing on iOS.** Android reads the text from `data`; iOS reads it from the APNs alert. Send both, they do not interfere:
|
|
278
339
|
|
|
279
340
|
```php
|
|
@@ -323,3 +384,6 @@ Do not test with the app added to the battery whitelist. That measures a phone y
|
|
|
323
384
|
| iOS registers but never gets a token | Firebase loaded after APNs answered | Section 8 |
|
|
324
385
|
| iOS receives the push and does nothing when tapped | Empty `callback` | Section 8 |
|
|
325
386
|
| Nothing arrives for twenty minutes, then five at once | OEM battery management | Section 9 |
|
|
387
|
+
| Push works on a fresh install and never on a reinstall | `didRefreshRegistrationToken` does not fire for a token Firebase already had | Section 1 |
|
|
388
|
+
| iOS: `fcmToken` is empty right after the permission is granted | It is; the APNs-for-FCM exchange has no fixed duration. Use `fetchToken` | Section 8 |
|
|
389
|
+
| iOS: `BadDeviceToken` when sending | `apnsToken` was assigned by hand instead of leaving it to the swizzling | Section 8 |
|
|
@@ -19,7 +19,7 @@ Community resources, support channels, tools, and learning materials for Titaniu
|
|
|
19
19
|
### Documentation
|
|
20
20
|
|
|
21
21
|
- Titanium SDK Docs: https://titaniumsdk.com/
|
|
22
|
-
- Alloy Framework Docs: https://titaniumsdk.com/
|
|
22
|
+
- Alloy Framework Docs: https://titaniumsdk.com/docs/alloy
|
|
23
23
|
- This skill (`ti-guides`): `../SKILL.md`
|
|
24
24
|
|
|
25
25
|
---
|
|
@@ -8,11 +8,12 @@ Each GA release desupports the previous patch release. Running an unsupported ve
|
|
|
8
8
|
## Contents
|
|
9
9
|
|
|
10
10
|
- [1. Version timeline](#1-version-timeline)
|
|
11
|
-
- [2. 13.4.
|
|
12
|
-
- [3. 13.
|
|
13
|
-
- [4. 13.3.
|
|
14
|
-
- [5. 13.
|
|
15
|
-
- [6.
|
|
11
|
+
- [2. 13.4.1 — patch](#2-1341--patch)
|
|
12
|
+
- [3. 13.4.0 — Android target SDK 36 groundwork](#3-1340--android-target-sdk-36-groundwork)
|
|
13
|
+
- [4. 13.3.1 — patch](#4-1331--patch)
|
|
14
|
+
- [5. 13.3.0 — Xcode 27, iOS 27, multi-scene](#5-1330--xcode-27-ios-27-multi-scene)
|
|
15
|
+
- [6. 13.2.0 — ViewPager2, keepHardwareMode](#6-1320--viewpager2-keephardwaremode)
|
|
16
|
+
- [7. Bundled module versions](#7-bundled-module-versions)
|
|
16
17
|
|
|
17
18
|
<!-- TOC-END -->
|
|
18
19
|
|
|
@@ -20,6 +21,7 @@ Each GA release desupports the previous patch release. Running an unsupported ve
|
|
|
20
21
|
|
|
21
22
|
| Version | Date | Headline |
|
|
22
23
|
|---|---|---|
|
|
24
|
+
| 13.4.1.GA | 25 August 2026 | Patch — BottomNavigation keyboard and androidback (Android), window-open deadlock (iOS) |
|
|
23
25
|
| 13.4.0.GA | 28 July 2026 | Prepares the SDK for Android target SDK 36; v8 memory cleanup |
|
|
24
26
|
| 13.3.1.GA | 20 July 2026 | Patch — iOS restart and LiveView fixes |
|
|
25
27
|
| 13.3.0.GA | 01 July 2026 | Xcode 27 and iOS 27, Hyperloop 8.0.0, iOS multi-scene apps |
|
|
@@ -27,7 +29,16 @@ Each GA release desupports the previous patch release. Running an unsupported ve
|
|
|
27
29
|
| 13.1.0.GA | — | Minor release, high-priority fixes |
|
|
28
30
|
| 13.0.0.GA | — | Major release: iOS 26 / Xcode 26, Android 16 KB page size |
|
|
29
31
|
|
|
30
|
-
## 2. 13.4.
|
|
32
|
+
## 2. 13.4.1 — patch
|
|
33
|
+
|
|
34
|
+
Released 25 August 2026. Two fixes, both to crashes and input handling rather than to any API:
|
|
35
|
+
|
|
36
|
+
- **Android:** keyboard issues in `BottomNavigation`, and the `androidback` event.
|
|
37
|
+
- **iOS:** a possible deadlock when opening a window.
|
|
38
|
+
|
|
39
|
+
Desupports 13.4.0. No API surface changed, so code written against 13.4.0 needs no edit.
|
|
40
|
+
|
|
41
|
+
## 3. 13.4.0 — Android target SDK 36 groundwork
|
|
31
42
|
|
|
32
43
|
- **Prepares the SDK for Android target SDK 36.** Groundwork only; the compatibility matrix still lists API 35 as the maximum target. See [compatibility-matrix.md](compatibility-matrix.md) § Android.
|
|
33
44
|
- v8 memory cleanup (Android).
|
|
@@ -36,11 +47,11 @@ Each GA release desupports the previous patch release. Running an unsupported ve
|
|
|
36
47
|
- iOS: fixed the dimming area in a TableView SearchBar.
|
|
37
48
|
- The debug-only selector for `Ti.App._restart()` was removed, making the method callable outside debug builds.
|
|
38
49
|
|
|
39
|
-
##
|
|
50
|
+
## 4. 13.3.1 — patch
|
|
40
51
|
|
|
41
52
|
iOS-only fixes: the `restart` method, and LiveView restart. Hyperloop bumped to 8.0.1.
|
|
42
53
|
|
|
43
|
-
##
|
|
54
|
+
## 5. 13.3.0 — Xcode 27, iOS 27, multi-scene
|
|
44
55
|
|
|
45
56
|
- **Xcode 27 and iOS 27 support.** This postdates the Xcode/iOS rows in the compatibility matrix, which still cap at Xcode 16.x.
|
|
46
57
|
- **iOS multi-scene applications** are supported.
|
|
@@ -51,7 +62,7 @@ iOS-only fixes: the `restart` method, and LiveView restart. Hyperloop bumped to
|
|
|
51
62
|
- `borderWidth` no longer renders at double thickness when `borderRadius` is an array (iOS).
|
|
52
63
|
- Android: Material 3 BottomNavigation theme and dark/light switching fixes, CameraX rotation fix under orientation lock, WebView fix for URLs carrying large GET data, `Ti.Preferences` optimized.
|
|
53
64
|
|
|
54
|
-
##
|
|
65
|
+
## 6. 13.2.0 — ViewPager2, keepHardwareMode
|
|
55
66
|
|
|
56
67
|
- **ScrollableView moved to ViewPager2 on Android.** A backing-widget swap; verify paging, nested scrolling and any custom paging control against it.
|
|
57
68
|
- **`keepHardwareMode` added to `Ti.UI.View`** (Android) — controls the view's render mode.
|
|
@@ -63,7 +74,7 @@ iOS-only fixes: the `restart` method, and LiveView restart. Hyperloop bumped to
|
|
|
63
74
|
- Node.js 24 support; core-js upgraded, removing the `baseline-browser-mapping` warning.
|
|
64
75
|
- iOS: Mac Catalyst fixes for `Ti.UI.Button` styling and events, and module builds with `mac=true`.
|
|
65
76
|
|
|
66
|
-
##
|
|
77
|
+
## 7. Bundled module versions
|
|
67
78
|
|
|
68
79
|
Versions shipped with each SDK. Unchanged rows are omitted.
|
|
69
80
|
|
|
@@ -72,4 +83,4 @@ Versions shipped with each SDK. Unchanged rows are omitted.
|
|
|
72
83
|
| facebook | 14.0.0 / 15.0.0 | 15.0.0 / 16.0.0 |
|
|
73
84
|
| hyperloop | 7.1.0 / 7.1.0 | 8.0.1 / 8.0.1 (8.0.0 in 13.3.0) |
|
|
74
85
|
|
|
75
|
-
Stable across 13.2.0–13.4.
|
|
86
|
+
Stable across 13.2.0–13.4.1: `ti.map` 5.7.0 / 7.3.1, `ti.webdialog` 2.5.0 / 3.0.2, `ti.playservices` 18.6.0 (Android only), `ti.identity` 3.2.0 / 5.0.0, `urlSession` 4.0.1 (iOS only), `ti.coremotion` 4.0.1 (iOS only), `ti.applesignin` 3.1.2 (iOS only).
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
- [3. Upgrading to SDK 9.0.0+ (Android)](#3-upgrading-to-sdk-900-android)
|
|
9
9
|
- [4. Hyperloop](#4-hyperloop)
|
|
10
10
|
- [3. Native module development](#3-native-module-development)
|
|
11
|
+
- [Swift Package Manager dependencies (iOS)](#swift-package-manager-dependencies-ios)
|
|
11
12
|
- [4. Choosing between Hyperloop and native modules](#4-choosing-between-hyperloop-and-native-modules)
|
|
12
13
|
- [5. Finding and using third-party modules](#5-finding-and-using-third-party-modules)
|
|
13
14
|
- [Best practices summary](#best-practices-summary)
|
|
@@ -601,6 +602,82 @@ View proxies (custom UI components):
|
|
|
601
602
|
@end
|
|
602
603
|
```
|
|
603
604
|
|
|
605
|
+
### Swift Package Manager dependencies (iOS)
|
|
606
|
+
|
|
607
|
+
Since Titanium SDK 13.1.0, an iOS module declares Swift package dependencies in an `spm.json` file and the CLI wires them into the consuming app. This replaces vendoring pre-built `.xcframework` files in `ios/platform` and replaces the legacy `hooks/ti.spm.js` hook.
|
|
608
|
+
|
|
609
|
+
Add the package to your module's Xcode project as usual so the module compiles against it, then declare the same package in `spm.json`, next to the module `manifest`:
|
|
610
|
+
|
|
611
|
+
```
|
|
612
|
+
mymodule/
|
|
613
|
+
└── ios/
|
|
614
|
+
├── manifest
|
|
615
|
+
├── spm.json
|
|
616
|
+
├── MyModule.xcodeproj
|
|
617
|
+
└── Classes/
|
|
618
|
+
```
|
|
619
|
+
|
|
620
|
+
```json
|
|
621
|
+
{
|
|
622
|
+
"dependencies": [
|
|
623
|
+
{
|
|
624
|
+
"remotePackageReference": "Parse-SDK-iOS-OSX",
|
|
625
|
+
"repositoryURL": "https://github.com/parse-community/Parse-SDK-iOS-OSX",
|
|
626
|
+
"requirementKind": "upToNextMajorVersion",
|
|
627
|
+
"requirementMinimumVersion": "5.1.1",
|
|
628
|
+
"products": [
|
|
629
|
+
{ "productName": "ParseObjC", "frameworkName": "ParseObjC", "linkage": "host" },
|
|
630
|
+
{ "productName": "ParseLiveQuery", "frameworkName": "ParseLiveQuery", "linkage": "embedded" }
|
|
631
|
+
]
|
|
632
|
+
}
|
|
633
|
+
]
|
|
634
|
+
}
|
|
635
|
+
```
|
|
636
|
+
|
|
637
|
+
#### Dependency fields
|
|
638
|
+
|
|
639
|
+
| Field | Required | Default | Description |
|
|
640
|
+
|---|---|---|---|
|
|
641
|
+
| `repositoryURL` | yes | — | Git URL of the Swift package. A dependency without it is skipped with a warning. |
|
|
642
|
+
| `remotePackageReference` | no | last path segment of `repositoryURL` | Reference name in the generated Xcode project. |
|
|
643
|
+
| `requirementKind` | no | `upToNextMajorVersion` | Version resolution rule, e.g. `upToNextMajorVersion`, `upToNextMinorVersion`. |
|
|
644
|
+
| `requirementMinimumVersion` | no | `1.0.0` | Minimum package version to resolve. |
|
|
645
|
+
| `linkage` | no | `embedded` | Default linkage for every product of this dependency. |
|
|
646
|
+
| `products` | yes | — | Non-empty array of products to link. A dependency with no valid product is skipped with a warning. |
|
|
647
|
+
|
|
648
|
+
#### Product fields
|
|
649
|
+
|
|
650
|
+
| Field | Required | Default | Description |
|
|
651
|
+
|---|---|---|---|
|
|
652
|
+
| `productName` | yes | — | Product name as the Swift package declares it. |
|
|
653
|
+
| `frameworkName` | no | `productName` | Framework name in the Frameworks build phase, when it differs. |
|
|
654
|
+
| `linkage` | no | the dependency's `linkage` | Per-product override: `embedded` or `host`. |
|
|
655
|
+
|
|
656
|
+
#### embedded vs host
|
|
657
|
+
|
|
658
|
+
`embedded` bakes the product into the module binary. The module stays self-contained and the app's Xcode project is untouched. Use it for small, module-private dependencies.
|
|
659
|
+
|
|
660
|
+
`host` makes the CLI add the package to the app's Xcode project at build time, so the dependency exists once in the app. Use it when several modules share the same package, or when the package ships resources or dynamic frameworks the app must embed.
|
|
661
|
+
|
|
662
|
+
If two modules request the same package, it is added once. If they request conflicting version requirements, the CLI warns and uses the first requirement it sees.
|
|
663
|
+
|
|
664
|
+
#### Building
|
|
665
|
+
|
|
666
|
+
```bash
|
|
667
|
+
cd mymodule/ios
|
|
668
|
+
ti build -p ios --build-only
|
|
669
|
+
```
|
|
670
|
+
|
|
671
|
+
The zip in `dist/` now carries the package information in its `metadata.json`. App developers install the module normally; nothing extra is required of them. During an app build, lines prefixed with `[SPM]` say what was added:
|
|
672
|
+
|
|
673
|
+
```
|
|
674
|
+
[INFO] [SPM] Will add 1 Swift package(s) to the app project
|
|
675
|
+
```
|
|
676
|
+
|
|
677
|
+
#### Migrating off ti.spm.js
|
|
678
|
+
|
|
679
|
+
A module that ships both `spm.json` and `hooks/ti.spm.js` has the hook excluded from its packaged zip automatically. A module that still ships only the hook makes the app build log a warning. To migrate, translate the hook's package information into `spm.json`, delete `hooks/ti.spm.js`, and rebuild.
|
|
680
|
+
|
|
604
681
|
### Module distribution
|
|
605
682
|
|
|
606
683
|
#### Packaging
|