@maccesar/titools 2.2.4 → 2.2.9

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 (98) hide show
  1. package/README.md +23 -23
  2. package/agents/ti-pro.md +17 -17
  3. package/lib/commands/update.js +6 -14
  4. package/lib/downloader.js +17 -2
  5. package/package.json +1 -1
  6. package/skills/alloy-guides/SKILL.md +31 -31
  7. package/skills/alloy-guides/references/CONCEPTS.md +3 -3
  8. package/skills/alloy-guides/references/CONTROLLERS.md +1 -1
  9. package/skills/alloy-guides/references/MODELS.md +6 -6
  10. package/skills/alloy-guides/references/VIEWS_WITHOUT_CONTROLLERS.md +1 -1
  11. package/skills/alloy-guides/references/VIEWS_XML.md +1 -1
  12. package/skills/alloy-guides/references/WIDGETS.md +1 -1
  13. package/skills/alloy-howtos/SKILL.md +27 -27
  14. package/skills/alloy-howtos/references/best_practices.md +9 -9
  15. package/skills/alloy-howtos/references/cli_reference.md +14 -14
  16. package/skills/alloy-howtos/references/config_files.md +16 -16
  17. package/skills/alloy-howtos/references/custom_tags.md +16 -16
  18. package/skills/alloy-howtos/references/debugging_troubleshooting.md +11 -15
  19. package/skills/alloy-howtos/references/samples.md +19 -19
  20. package/skills/purgetss/SKILL.md +11 -1
  21. package/skills/purgetss/references/animation-system.md +1 -1
  22. package/skills/purgetss/references/cli-commands.md +3 -3
  23. package/skills/purgetss/references/customization-deep-dive.md +1 -1
  24. package/skills/purgetss/references/dynamic-component-creation.md +1 -1
  25. package/skills/purgetss/references/icon-fonts.md +4 -0
  26. package/skills/purgetss/references/installation-setup.md +8 -1
  27. package/skills/purgetss/references/migration-guide.md +4 -0
  28. package/skills/purgetss/references/tikit-components.md +193 -204
  29. package/skills/purgetss/references/ui-ux-design.md +1 -1
  30. package/skills/ti-expert/SKILL.md +78 -118
  31. package/skills/ti-expert/references/alloy-builtins.md +18 -18
  32. package/skills/ti-expert/references/alloy-structure.md +21 -21
  33. package/skills/ti-expert/references/anti-patterns.md +15 -15
  34. package/skills/ti-expert/references/cli-expert.md +15 -15
  35. package/skills/ti-expert/references/code-conventions.md +38 -38
  36. package/skills/ti-expert/references/contracts.md +8 -8
  37. package/skills/ti-expert/references/controller-patterns.md +14 -14
  38. package/skills/ti-expert/references/error-handling.md +11 -11
  39. package/skills/ti-expert/references/examples.md +12 -12
  40. package/skills/ti-expert/references/migration-patterns.md +24 -24
  41. package/skills/ti-expert/references/patterns.md +10 -10
  42. package/skills/ti-expert/references/performance-listview.md +16 -16
  43. package/skills/ti-expert/references/performance-optimization.md +41 -41
  44. package/skills/ti-expert/references/security-device.md +22 -22
  45. package/skills/ti-expert/references/security-fundamentals.md +19 -19
  46. package/skills/ti-expert/references/state-management.md +33 -33
  47. package/skills/ti-expert/references/testing-e2e-ci.md +25 -25
  48. package/skills/ti-expert/references/testing-unit.md +24 -24
  49. package/skills/ti-expert/references/theming.md +15 -15
  50. package/skills/ti-guides/SKILL.md +58 -60
  51. package/skills/ti-guides/references/advanced-data-and-images.md +33 -33
  52. package/skills/ti-guides/references/android-manifest.md +15 -15
  53. package/skills/ti-guides/references/app-distribution.md +70 -166
  54. package/skills/ti-guides/references/application-frameworks.md +96 -114
  55. package/skills/ti-guides/references/cli-reference.md +294 -294
  56. package/skills/ti-guides/references/coding-best-practices.md +42 -33
  57. package/skills/ti-guides/references/commonjs-advanced.md +57 -51
  58. package/skills/ti-guides/references/hello-world.md +36 -36
  59. package/skills/ti-guides/references/hyperloop-native-access.md +66 -66
  60. package/skills/ti-guides/references/javascript-primer.md +83 -101
  61. package/skills/ti-guides/references/reserved-words.md +9 -9
  62. package/skills/ti-guides/references/resources.md +75 -83
  63. package/skills/ti-guides/references/style-and-conventions.md +35 -28
  64. package/skills/ti-guides/references/tiapp-config.md +110 -74
  65. package/skills/ti-howtos/SKILL.md +88 -92
  66. package/skills/ti-howtos/references/android-platform-deep-dives.md +104 -104
  67. package/skills/ti-howtos/references/automation-fastlane-appium.md +39 -39
  68. package/skills/ti-howtos/references/buffer-codec-streams.md +60 -60
  69. package/skills/ti-howtos/references/cross-platform-development.md +115 -136
  70. package/skills/ti-howtos/references/debugging-profiling.md +167 -181
  71. package/skills/ti-howtos/references/extending-titanium.md +121 -121
  72. package/skills/ti-howtos/references/google-maps-v2.md +84 -82
  73. package/skills/ti-howtos/references/ios-map-kit.md +65 -60
  74. package/skills/ti-howtos/references/ios-platform-deep-dives.md +123 -123
  75. package/skills/ti-howtos/references/local-data-sources.md +79 -78
  76. package/skills/ti-howtos/references/location-and-maps.md +116 -120
  77. package/skills/ti-howtos/references/media-apis.md +87 -86
  78. package/skills/ti-howtos/references/notification-services.md +250 -260
  79. package/skills/ti-howtos/references/remote-data-sources.md +98 -93
  80. package/skills/ti-howtos/references/tutorials.md +226 -216
  81. package/skills/ti-howtos/references/using-modules.md +73 -102
  82. package/skills/ti-howtos/references/web-content-integration.md +101 -103
  83. package/skills/ti-howtos/references/webpack-build-pipeline.md +52 -44
  84. package/skills/ti-ui/SKILL.md +85 -85
  85. package/skills/ti-ui/references/accessibility-deep-dive.md +57 -57
  86. package/skills/ti-ui/references/animation-and-matrices.md +79 -79
  87. package/skills/ti-ui/references/application-structures.md +96 -99
  88. package/skills/ti-ui/references/custom-fonts-styling.md +99 -99
  89. package/skills/ti-ui/references/event-handling.md +58 -58
  90. package/skills/ti-ui/references/gestures.md +62 -64
  91. package/skills/ti-ui/references/icons-and-splash-screens.md +88 -88
  92. package/skills/ti-ui/references/layouts-and-positioning.md +87 -97
  93. package/skills/ti-ui/references/listviews-and-performance.md +107 -107
  94. package/skills/ti-ui/references/orientation.md +87 -88
  95. package/skills/ti-ui/references/platform-ui-android.md +87 -81
  96. package/skills/ti-ui/references/platform-ui-ios.md +63 -61
  97. package/skills/ti-ui/references/scrolling-views.md +29 -29
  98. package/skills/ti-ui/references/tableviews.md +56 -56
@@ -1,36 +1,36 @@
1
- # Push and Local Notifications
1
+ # Push and local notifications
2
2
 
3
- Comprehensive guide for local and push notifications on iOS and Android.
3
+ Guide to local and push notifications on iOS and Android.
4
4
 
5
- ## Local Notifications Android
5
+ ## Local notifications: Android
6
6
 
7
- ### Notification Display Locations
7
+ ### Notification display locations
8
8
 
9
- - **Notification Drawer**: Swipe down from status bar
10
- - **Lock Screen**: Android 5.0+ (API 21+)
11
- - **Status Bar**: Icon and ticker text
9
+ - Notification drawer: swipe down from status bar
10
+ - Lock screen: Android 5.0+ (API 21+)
11
+ - Status bar: icon and ticker text
12
12
 
13
- ### Create a Notification
13
+ ### Create a notification
14
14
 
15
- **Basic Layout:**
15
+ Basic layout:
16
16
  ```javascript
17
17
  const notification = Ti.Android.createNotification({
18
- contentTitle: 'Notification Title',
19
- contentText: 'Notification message',
20
- contentIntent: Ti.Android.createPendingIntent({
21
- intent: Ti.Android.createIntent({})
22
- }),
23
- icon: Ti.App.Android.R.drawable.warn, // or '/images/warn.png'
24
- number: 5, // Badge number
25
- when: new Date(), // Timestamp (does NOT schedule)
26
- tickerText: 'Text in status bar when notification first appears'
18
+ contentTitle: 'Notification Title',
19
+ contentText: 'Notification message',
20
+ contentIntent: Ti.Android.createPendingIntent({
21
+ intent: Ti.Android.createIntent({})
22
+ }),
23
+ icon: Ti.App.Android.R.drawable.warn, // or '/images/warn.png'
24
+ number: 5, // Badge number
25
+ when: new Date(), // Timestamp (does not schedule)
26
+ tickerText: 'Text in status bar when notification first appears'
27
27
  });
28
28
 
29
29
  // Send immediately
30
30
  Ti.Android.NotificationManager.notify(1, notification);
31
31
  ```
32
32
 
33
- **Icon paths:**
33
+ Icon paths:
34
34
  - Density-specific: `/app/assets/android/images/` (Alloy) or `/Resources/android/images/` (Classic), reference as `/images/filename.png`
35
35
  - Drawable folder: `/platform/android/res/drawable/filename.png`, reference as `Ti.App.Android.R.drawable.filename`
36
36
 
@@ -38,8 +38,8 @@ Ti.Android.NotificationManager.notify(1, notification);
38
38
 
39
39
  ```javascript
40
40
  const notification = Ti.Android.createNotification({
41
- // ... other properties
42
- sound: `${Ti.Filesystem.getResRawDirectory()}sound.wav`
41
+ // ... other properties
42
+ sound: `${Ti.Filesystem.getResRawDirectory()}sound.wav`
43
43
  });
44
44
  ```
45
45
 
@@ -48,9 +48,9 @@ Play only once (add flag):
48
48
  notification.flags |= Ti.Android.FLAG_ONLY_ALERT_ONCE;
49
49
  ```
50
50
 
51
- ### Custom Layout with RemoteViews
51
+ ### Custom layout with RemoteViews
52
52
 
53
- **1. Create XML Layout** (`/platform/android/res/layout/customview.xml`):
53
+ 1. Create XML layout (`/platform/android/res/layout/customview.xml`):
54
54
  ```xml
55
55
  <?xml version="1.0" encoding="utf-8"?>
56
56
  <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
@@ -68,64 +68,64 @@ notification.flags |= Ti.Android.FLAG_ONLY_ALERT_ONCE;
68
68
  </LinearLayout>
69
69
  ```
70
70
 
71
- **2. Create RemoteViews and bind intents:**
71
+ 2. Create RemoteViews and bind intents:
72
72
  ```javascript
73
73
  const customView = Ti.Android.createRemoteViews({
74
- layoutId: Ti.App.Android.R.layout.customview
74
+ layoutId: Ti.App.Android.R.layout.customview
75
75
  });
76
76
 
77
77
  // Modify text
78
- customView.setTextViewText(Ti.App.Android.R.id.message, "Update available!");
78
+ customView.setTextViewText(Ti.App.Android.R.id.message, 'Update available!');
79
79
 
80
80
  // Bind intents to buttons
81
- const downloadIntent = Ti.Android.createPendingIntent({intent: Ti.Android.createIntent()});
81
+ const downloadIntent = Ti.Android.createPendingIntent({ intent: Ti.Android.createIntent() });
82
82
  customView.setOnClickPendingIntent(Ti.App.Android.R.id.okbutton, downloadIntent);
83
83
 
84
84
  const notification = Ti.Android.createNotification({
85
- contentView: customView // Avoid setting contentTitle/contentText or they override this
85
+ contentView: customView // Avoid setting contentTitle/contentText or they override this
86
86
  });
87
87
  ```
88
88
 
89
- ### Additional Properties
89
+ ### Additional properties
90
90
 
91
- **Defaults (device settings):**
91
+ Defaults (device settings):
92
92
  ```javascript
93
- notification.defaults = Ti.Android.DEFAULT_ALL; // or DEFAULT_SOUND, DEFAULT_VIBRATE, DEFAULT_LIGHTS
93
+ notification.defaults = Ti.Android.DEFAULT_ALL; // or DEFAULT_SOUND, DEFAULT_VIBRATE, DEFAULT_LIGHTS
94
94
  ```
95
95
 
96
- **Flags (behavior):**
96
+ Flags (behavior):
97
97
  ```javascript
98
- notification.flags |= Ti.Android.FLAG_AUTO_CANCEL; // Clear on tap
99
- notification.flags |= Ti.Android.FLAG_INSISTENT; // Repeat sound until canceled
100
- notification.flags |= Ti.Android.FLAG_NO_CLEAR; // Don't clear with "clear all"
101
- notification.flags |= Ti.Android.FLAG_ONGOING_EVENT; // Ongoing event (e.g., music player)
102
- notification.flags |= Ti.Android.FLAG_ONLY_ALERT_ONCE; // Don't replay sound/vibrate
103
- notification.flags |= Ti.Android.FLAG_SHOW_LIGHTS; // Use LED (if device allows)
98
+ notification.flags |= Ti.Android.FLAG_AUTO_CANCEL; // Clear on tap
99
+ notification.flags |= Ti.Android.FLAG_INSISTENT; // Repeat sound until canceled
100
+ notification.flags |= Ti.Android.FLAG_NO_CLEAR; // Do not clear with "clear all"
101
+ notification.flags |= Ti.Android.FLAG_ONGOING_EVENT; // Ongoing event (for example, music player)
102
+ notification.flags |= Ti.Android.FLAG_ONLY_ALERT_ONCE; // Do not replay sound/vibrate
103
+ notification.flags |= Ti.Android.FLAG_SHOW_LIGHTS; // Use LED (if device allows)
104
104
  ```
105
105
 
106
- **Priority** (Android 4.1+):
106
+ Priority (Android 4.1+):
107
107
  ```javascript
108
- notification.priority = Ti.Android.PRIORITY_HIGH; // or PRIORITY_MAX, PRIORITY_DEFAULT, PRIORITY_LOW, PRIORITY_MIN
108
+ notification.priority = Ti.Android.PRIORITY_HIGH; // or PRIORITY_MAX, PRIORITY_DEFAULT, PRIORITY_LOW, PRIORITY_MIN
109
109
  ```
110
110
 
111
- **Category** (Android 5.0+):
111
+ Category (Android 5.0+):
112
112
  ```javascript
113
- notification.category = Ti.Android.CATEGORY_MESSAGE; // or CATEGORY_ALARM, CALL, EMAIL, ERROR, EVENT, etc.
113
+ notification.category = Ti.Android.CATEGORY_MESSAGE; // or CATEGORY_ALARM, CALL, EMAIL, ERROR, EVENT, etc.
114
114
  ```
115
115
 
116
- **Visibility** (Android 5.0+ - lock screen):
116
+ Visibility (Android 5.0+ - lock screen):
117
117
  ```javascript
118
- notification.visibility = Ti.Android.VISIBILITY_PUBLIC; // Show all
119
- // or VISIBILITY_PRIVATE (hide content), VISIBILITY_SECRET (don't show)
118
+ notification.visibility = Ti.Android.VISIBILITY_PUBLIC; // Show all
119
+ // or VISIBILITY_PRIVATE (hide content), VISIBILITY_SECRET (do not show)
120
120
  ```
121
121
 
122
- ### Update Notification
122
+ ### Update notification
123
123
 
124
124
  ```javascript
125
125
  notification.setLatestEventInfo('New Title', 'New Message', notification.contentIntent);
126
126
  ```
127
127
 
128
- ### Remove Notifications
128
+ ### Remove notifications
129
129
 
130
130
  ```javascript
131
131
  // Remove specific notification
@@ -135,35 +135,35 @@ Ti.Android.NotificationManager.cancel(1);
135
135
  Ti.Android.NotificationManager.cancelAll();
136
136
  ```
137
137
 
138
- ### Respond to Notification Tap
138
+ ### Respond to notification tap
139
139
 
140
- **Launch app when tapped:**
140
+ Launch app when tapped:
141
141
  ```javascript
142
142
  const intent = Ti.Android.createIntent({
143
- action: Ti.Android.ACTION_MAIN,
144
- className: 'com.titaniumsdk.testapp.MyappActivity', // ProjectName + Activity
145
- packageName: 'com.titaniumsdk.testapp'
143
+ action: Ti.Android.ACTION_MAIN,
144
+ className: 'com.titaniumsdk.testapp.MyappActivity', // ProjectName + Activity
145
+ packageName: 'com.titaniumsdk.testapp'
146
146
  });
147
147
  intent.flags |= Ti.Android.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED | Ti.Android.FLAG_ACTIVITY_SINGLE_TOP;
148
148
  intent.addCategory(Ti.Android.CATEGORY_LAUNCHER);
149
149
 
150
- notification.contentIntent = Ti.Android.createPendingIntent({intent: intent});
150
+ notification.contentIntent = Ti.Android.createPendingIntent({ intent: intent });
151
151
  ```
152
152
 
153
- ### Delete Intent (Notification Cleared)
153
+ ### Delete intent (notification cleared)
154
154
 
155
155
  ```javascript
156
156
  // Execute intent when user clears notification
157
157
  notification.deleteIntent = Ti.Android.createPendingIntent({
158
- intent: Ti.Android.createIntent({
159
- action: 'com.myapp.NOTIFICATION_CLEARED'
160
- })
158
+ intent: Ti.Android.createIntent({
159
+ action: 'com.myapp.NOTIFICATION_CLEARED'
160
+ })
161
161
  });
162
162
  ```
163
163
 
164
- ### Schedule Future Notification (Background Service)
164
+ ### Schedule future notification (background service)
165
165
 
166
- **tiapp.xml:**
166
+ `tiapp.xml`:
167
167
  ```xml
168
168
  <android xmlns:android="http://schemas.android.com/apk/res/android">
169
169
  <services>
@@ -172,379 +172,369 @@ notification.deleteIntent = Ti.Android.createPendingIntent({
172
172
  </android>
173
173
  ```
174
174
 
175
- **ExampleService.js:**
175
+ `ExampleService.js`:
176
176
  ```javascript
177
177
  const service = Ti.Android.currentService;
178
178
  const serviceIntent = service.getIntent();
179
179
  const timestamp = new Date(serviceIntent.getStringExtra('timestamp'));
180
180
 
181
181
  if (new Date() > timestamp) {
182
- const title = serviceIntent.getStringExtra('title');
183
- const message = serviceIntent.getStringExtra('message');
182
+ const title = serviceIntent.getStringExtra('title');
183
+ const message = serviceIntent.getStringExtra('message');
184
184
 
185
- const notification = Ti.Android.createNotification({
186
- contentTitle: title,
187
- contentText: message,
188
- contentIntent: Ti.Android.createPendingIntent({intent: Ti.Android.createIntent()})
189
- });
185
+ const notification = Ti.Android.createNotification({
186
+ contentTitle: title,
187
+ contentText: message,
188
+ contentIntent: Ti.Android.createPendingIntent({ intent: Ti.Android.createIntent() })
189
+ });
190
190
 
191
- Ti.Android.NotificationManager.notify(1, notification);
192
- Ti.Android.stopService(serviceIntent);
191
+ Ti.Android.NotificationManager.notify(1, notification);
192
+ Ti.Android.stopService(serviceIntent);
193
193
  }
194
194
  ```
195
195
 
196
- **Main app:**
196
+ Main app:
197
197
  ```javascript
198
- const intent = Ti.Android.createServiceIntent({url: 'ExampleService.js'});
199
- intent.putExtra('interval', 5000); // Check every 5 seconds
200
- intent.putExtra('timestamp', new Date(new Date().getTime() + (30 * 1000))); // Fire in 30 seconds
198
+ const intent = Ti.Android.createServiceIntent({ url: 'ExampleService.js' });
199
+ intent.putExtra('interval', 5000); // Check every 5 seconds
200
+ intent.putExtra('timestamp', new Date(new Date().getTime() + (30 * 1000))); // Fire in 30 seconds
201
201
  intent.putExtra('title', 'Scheduled Notification');
202
202
  intent.putExtra('message', 'This was scheduled!');
203
203
  Ti.Android.startService(intent);
204
204
  ```
205
205
 
206
- ---
207
-
208
- ## Local Notifications iOS
206
+ ## Local notifications: iOS
209
207
 
210
- ### Notification Display Locations
208
+ ### Notification display locations
211
209
 
212
- - **Alert Dialog**: "Open" or "Close" buttons (background, unlocked)
213
- - **Banner Message**: Swipe down for actions, tap to launch (background, unlocked)
214
- - **Lock Screen**: Swipe right to launch
215
- - **Notification Center**: Queued notifications
216
- - **Badge**: Number on app icon
217
- - **Sound**: Audio alert
210
+ - Alert dialog: Open or Close buttons (background, unlocked)
211
+ - Banner message: swipe down for actions, tap to launch (background, unlocked)
212
+ - Lock screen: swipe right to launch
213
+ - Notification Center: queued notifications
214
+ - Badge: number on app icon
215
+ - Sound: audio alert
218
216
 
219
- ### Register for Notifications (iOS 8+)
217
+ ### Register for notifications (iOS 8+)
220
218
 
221
219
  ```javascript
222
220
  if (Ti.Platform.name === 'iPhone OS' && parseInt(Ti.Platform.version.split('.')[0]) >= 8) {
223
- Ti.App.iOS.registerUserNotificationSettings({
224
- types: [
225
- Ti.App.iOS.USER_NOTIFICATION_TYPE_ALERT,
226
- Ti.App.iOS.USER_NOTIFICATION_TYPE_SOUND,
227
- Ti.App.iOS.USER_NOTIFICATION_TYPE_BADGE
228
- ]
229
- // Add 'categories' for interactive notifications
230
- });
221
+ Ti.App.iOS.registerUserNotificationSettings({
222
+ types: [
223
+ Ti.App.iOS.USER_NOTIFICATION_TYPE_ALERT,
224
+ Ti.App.iOS.USER_NOTIFICATION_TYPE_SOUND,
225
+ Ti.App.iOS.USER_NOTIFICATION_TYPE_BADGE
226
+ ]
227
+ // Add 'categories' for interactive notifications
228
+ });
231
229
  }
232
230
  ```
233
231
 
234
- **Note**: iOS will show a system prompt asking the user to allow notifications on the first call. Check current settings with:
232
+ Note: iOS shows a system prompt the first time. Check current settings with:
235
233
  ```javascript
236
234
  Ti.App.iOS.addEventListener('usernotificationsettings', (e) => {
237
- Ti.API.info(`Notification types allowed: ${e.types}`);
235
+ Ti.API.info(`Notification types allowed: ${e.types}`);
238
236
  });
239
237
  ```
240
238
 
241
- ### Schedule Local Notification
239
+ ### Schedule local notification
242
240
 
243
241
  ```javascript
244
242
  const notification = Ti.App.iOS.scheduleLocalNotification({
245
- date: new Date(new Date().getTime() + 3000), // 3 seconds from now
246
- alertBody: 'New content available!',
247
- alertAction: 'update', // Changes "slide to view" to "slide to update" or "Open" to "Update"
248
- alertLaunchImage: 'splash.png', // Custom splash image
249
- badge: 1, // Set badge number (negative to clear)
250
- sound: '/alert.wav', // Sound file in Resources or app/assets
251
- category: 'CATEGORY_ID', // For interactive notifications
252
- repeat: 'daily', // or 'weekly', 'monthly', 'yearly'
253
- timezone: 'America/Los_Angeles', // Optional (default: system timezone)
254
- userInfo: { url: 'http://example.com/data.json', id: '123' } // Custom data
243
+ date: new Date(new Date().getTime() + 3000), // 3 seconds from now
244
+ alertBody: 'New content available!',
245
+ alertAction: 'update', // Changes "slide to view" to "slide to update" or "Open" to "Update"
246
+ alertLaunchImage: 'splash.png', // Custom splash image
247
+ badge: 1, // Set badge number (negative to clear)
248
+ sound: '/alert.wav', // Sound file in Resources or app/assets
249
+ category: 'CATEGORY_ID', // For interactive notifications
250
+ repeat: 'daily', // or 'weekly', 'monthly', 'yearly'
251
+ timezone: 'America/Los_Angeles', // Optional (default: system timezone)
252
+ userInfo: { url: 'http://example.com/data.json', id: '123' } // Custom data
255
253
  });
256
254
  ```
257
255
 
258
- ### Monitor Notifications
256
+ ### Monitor notifications
259
257
 
260
- **While app is in foreground or returns to foreground:**
258
+ While app is in foreground or returns to foreground:
261
259
  ```javascript
262
260
  Ti.App.iOS.addEventListener('notification', (e) => {
263
- // Process custom data
264
- if (e.userInfo && e.userInfo.url) {
265
- // Handle URL
266
- }
267
-
268
- // Reset badge
269
- if (e.badge > 0) {
270
- Ti.App.iOS.scheduleLocalNotification({
271
- date: new Date(),
272
- badge: -1
273
- });
274
- }
261
+ // Process custom data
262
+ if (e.userInfo && e.userInfo.url) {
263
+ // Handle URL
264
+ }
265
+
266
+ // Reset badge
267
+ if (e.badge > 0) {
268
+ Ti.App.iOS.scheduleLocalNotification({
269
+ date: new Date(),
270
+ badge: -1
271
+ });
272
+ }
275
273
  });
276
274
  ```
277
275
 
278
- ### Cancel Notifications
276
+ ### Cancel notifications
279
277
 
280
278
  ```javascript
281
279
  // Cancel all
282
280
  Ti.App.iOS.cancelAllLocalNotifications();
283
281
 
284
282
  // Cancel specific notification
285
- const notification = Ti.App.iOS.scheduleLocalNotification({...});
283
+ const notification = Ti.App.iOS.scheduleLocalNotification({ ... });
286
284
  notification.cancel();
287
285
 
288
286
  // Or by ID
289
287
  Ti.App.iOS.scheduleLocalNotification({
290
- userInfo: {id: 'foo'},
291
- alertBody: 'Test'
288
+ userInfo: { id: 'foo' },
289
+ alertBody: 'Test'
292
290
  });
293
291
  Ti.App.iOS.cancelLocalNotification('foo');
294
292
  ```
295
293
 
296
- ---
294
+ ## Interactive notifications: iOS (iOS 8+)
297
295
 
298
- ## Interactive Notifications iOS (iOS 8+)
299
-
300
- ### Create Notification Actions
296
+ ### Create notification actions
301
297
 
302
298
  ```javascript
303
299
  const acceptAction = Ti.App.iOS.createUserNotificationAction({
304
- identifier: 'ACCEPT_IDENTIFIER',
305
- title: 'Accept',
306
- activationMode: Ti.App.iOS.USER_NOTIFICATION_ACTIVATION_MODE_FOREGROUND, // or BACKGROUND
307
- destructive: false,
308
- authenticationRequired: true // Require device unlock
300
+ identifier: 'ACCEPT_IDENTIFIER',
301
+ title: 'Accept',
302
+ activationMode: Ti.App.iOS.USER_NOTIFICATION_ACTIVATION_MODE_FOREGROUND, // or BACKGROUND
303
+ destructive: false,
304
+ authenticationRequired: true // Require device unlock
309
305
  });
310
306
 
311
307
  const rejectAction = Ti.App.iOS.createUserNotificationAction({
312
- identifier: 'REJECT_IDENTIFIER',
313
- title: 'Reject',
314
- activationMode: Ti.App.iOS.USER_NOTIFICATION_ACTIVATION_MODE_BACKGROUND,
315
- destructive: true, // Red background in lock screen/notification center
316
- authenticationRequired: false
308
+ identifier: 'REJECT_IDENTIFIER',
309
+ title: 'Reject',
310
+ activationMode: Ti.App.iOS.USER_NOTIFICATION_ACTIVATION_MODE_BACKGROUND,
311
+ destructive: true, // Red background in lock screen/notification center
312
+ authenticationRequired: false
317
313
  });
318
314
  ```
319
315
 
320
- ### Create Notification Category
316
+ ### Create notification category
321
317
 
322
318
  ```javascript
323
319
  const category = Ti.App.iOS.createUserNotificationCategory({
324
- identifier: 'INVITE_CATEGORY',
325
- actionsForDefaultContext: [acceptAction, rescheduleAction, delayAction, rejectAction], // Alert dialog (4 max)
326
- actionsForMinimalContext: [acceptAction, rejectAction] // Other styles (2 max)
320
+ identifier: 'INVITE_CATEGORY',
321
+ actionsForDefaultContext: [acceptAction, rescheduleAction, delayAction, rejectAction], // Alert dialog (4 max)
322
+ actionsForMinimalContext: [acceptAction, rejectAction] // Other styles (2 max)
327
323
  });
328
324
  ```
329
325
 
330
- ### Register Categories
326
+ ### Register categories
331
327
 
332
328
  ```javascript
333
329
  Ti.App.iOS.registerUserNotificationSettings({
334
- types: [
335
- Ti.App.iOS.USER_NOTIFICATION_TYPE_ALERT,
336
- Ti.App.iOS.USER_NOTIFICATION_TYPE_BADGE,
337
- Ti.App.iOS.USER_NOTIFICATION_TYPE_SOUND
338
- ],
339
- categories: [category]
330
+ types: [
331
+ Ti.App.iOS.USER_NOTIFICATION_TYPE_ALERT,
332
+ Ti.App.iOS.USER_NOTIFICATION_TYPE_BADGE,
333
+ Ti.App.iOS.USER_NOTIFICATION_TYPE_SOUND
334
+ ],
335
+ categories: [category]
340
336
  });
341
337
  ```
342
338
 
343
- ### Monitor Interactive Notifications
339
+ ### Monitor interactive notifications
344
340
 
345
- **Local notifications:**
341
+ Local notifications:
346
342
  ```javascript
347
343
  Ti.App.iOS.addEventListener('localnotificationaction', (e) => {
348
- // e.category: category identifier
349
- // e.identifier: action identifier
350
- // e.userInfo: custom data
351
-
352
- if (e.category === 'INVITE_CATEGORY' && e.identifier === 'ACCEPT_IDENTIFIER') {
353
- // Handle accept
354
- if (e.userInfo && e.userInfo.url) {
355
- // Process URL
356
- }
357
- }
344
+ // e.category: category identifier
345
+ // e.identifier: action identifier
346
+ // e.userInfo: custom data
358
347
 
359
- // Reset badge
360
- if (e.badge > 0) {
361
- Ti.App.iOS.scheduleLocalNotification({ date: new Date(), badge: -1 });
348
+ if (e.category === 'INVITE_CATEGORY' && e.identifier === 'ACCEPT_IDENTIFIER') {
349
+ // Handle accept
350
+ if (e.userInfo && e.userInfo.url) {
351
+ // Process URL
362
352
  }
353
+ }
354
+
355
+ // Reset badge
356
+ if (e.badge > 0) {
357
+ Ti.App.iOS.scheduleLocalNotification({ date: new Date(), badge: -1 });
358
+ }
363
359
  });
364
360
  ```
365
361
 
366
- **Push notifications:**
362
+ Push notifications:
367
363
  ```javascript
368
364
  Ti.App.iOS.addEventListener('remotenotificationaction', (e) => {
369
- // e.category: category identifier
370
- // e.identifier: action identifier
371
- // e.data: push notification payload (JSON)
372
-
373
- if (e.category === 'DOWNLOAD_CATEGORY' && e.identifier === 'ACCEPT_IDENTIFIER') {
374
- if (e.data && e.data.url) {
375
- // Process URL
376
- }
365
+ // e.category: category identifier
366
+ // e.identifier: action identifier
367
+ // e.data: push notification payload (JSON)
368
+
369
+ if (e.category === 'DOWNLOAD_CATEGORY' && e.identifier === 'ACCEPT_IDENTIFIER') {
370
+ if (e.data && e.data.url) {
371
+ // Process URL
377
372
  }
373
+ }
378
374
  });
379
375
  ```
380
376
 
381
- ### Schedule Interactive Local Notification
377
+ ### Schedule interactive local notification
382
378
 
383
379
  ```javascript
384
380
  Ti.App.iOS.scheduleLocalNotification({
385
- date: new Date(new Date().getTime() + 3000),
386
- alertBody: 'New content available! Download now?',
387
- badge: 1,
388
- userInfo: {url: 'http://example.com/data.json'},
389
- category: 'INVITE_CATEGORY' // Must match registered category identifier
381
+ date: new Date(new Date().getTime() + 3000),
382
+ alertBody: 'New content available! Download now?',
383
+ badge: 1,
384
+ userInfo: { url: 'http://example.com/data.json' },
385
+ category: 'INVITE_CATEGORY' // Must match registered category identifier
390
386
  });
391
387
  ```
392
388
 
393
- ### Send Interactive Push Notification
389
+ ### Send interactive push notification
394
390
 
395
- **Payload format:**
391
+ Payload format:
396
392
  ```json
397
393
  {
398
- "alert": "New content available! Download now?",
399
- "badge": 1,
400
- "url": "http://example.com/data.json",
401
- "category": "INVITE_CATEGORY"
394
+ "alert": "New content available! Download now?",
395
+ "badge": 1,
396
+ "url": "http://example.com/data.json",
397
+ "category": "INVITE_CATEGORY"
402
398
  }
403
399
  ```
404
400
 
405
- ---
406
-
407
- ## Push Notifications
401
+ ## Push notifications
408
402
 
409
403
  ### Overview
410
404
 
411
405
  Push notifications allow server-to-device communication. Infrastructure differs by platform:
412
406
 
413
- - **Android**: Firebase Cloud Messaging (FCM)
414
- - **iOS**: Apple Push Notification service (APNs)
407
+ - Android: Firebase Cloud Messaging (FCM)
408
+ - iOS: Apple Push Notification service (APNs)
415
409
 
416
- > **Note**: The legacy Appcelerator Cloud Services (ACS/ArrowDB) push backend is no longer available. Use Firebase Cloud Messaging (FCM) for Android and Apple Push Notification service (APNs) for iOS, either directly or through a third-party service.
410
+ Note: the legacy Appcelerator Cloud Services (ACS/ArrowDB) push backend is no longer available. Use FCM for Android and APNs for iOS, either directly or via a third-party service.
417
411
 
418
- > Google has deprecated Google Cloud Messaging (GCM) in favor of Firebase Cloud Messaging (FCM). All new implementations should use FCM.
412
+ Google has deprecated Google Cloud Messaging (GCM) in favor of FCM. All new implementations should use FCM.
419
413
 
420
- For FCM on Titanium, use the community Firebase Cloud Messaging module: `titanium-firebase-cloud-messaging` (by Hans Knöchel).
414
+ For FCM on Titanium, use the community module `titanium-firebase-cloud-messaging` (by Hans Knöchel).
421
415
 
422
- ### Register for Push Notifications
416
+ ### Register for push notifications
423
417
 
424
418
  ```javascript
425
419
  Ti.Network.registerForPushNotifications({
426
- success: (e) => {
427
- // e.deviceToken: token to send to your push server
428
- Ti.API.info(`Device Token: ${e.deviceToken}`);
429
- // Send this token to your server
430
- },
431
- error: (e) => {
432
- Ti.API.error(`Push registration error: ${e.error}`);
433
- },
434
- callback: (e) => {
435
- // Incoming push notification
436
- // e.data: push payload (iOS)
437
- // e.data: JSON object (Android)
438
- Ti.API.info(`Push received: ${JSON.stringify(e.data)}`);
439
- }
420
+ success: (e) => {
421
+ // e.deviceToken: token to send to your push server
422
+ Ti.API.info(`Device Token: ${e.deviceToken}`);
423
+ // Send this token to your server
424
+ },
425
+ error: (e) => {
426
+ Ti.API.error(`Push registration error: ${e.error}`);
427
+ },
428
+ callback: (e) => {
429
+ // Incoming push notification
430
+ // e.data: push payload (iOS)
431
+ // e.data: JSON object (Android)
432
+ Ti.API.info(`Push received: ${JSON.stringify(e.data)}`);
433
+ }
440
434
  });
441
435
  ```
442
436
 
443
- ### APNs Configuration (iOS)
437
+ ### APNs configuration (iOS)
444
438
 
445
- 1. Register an **Explicit App ID** (not Wildcard) with Push Notifications enabled in Apple Developer Portal
446
- 2. Generate an APNs certificate (Development for sandbox, Production for App Store)
447
- 3. Export the certificate as PKCS#12 (.p12) from Keychain Access
448
- 4. Upload the .p12 to your push service provider
449
- 5. In your app, call `Ti.Network.registerForPushNotifications()` to get the device token
439
+ 1. Register an explicit App ID (not wildcard) with Push Notifications enabled in Apple Developer Portal.
440
+ 2. Generate an APNs certificate (Development for sandbox, Production for App Store).
441
+ 3. Export the certificate as PKCS#12 (.p12) from Keychain Access.
442
+ 4. Upload the .p12 to your push service provider.
443
+ 5. In your app, call `Ti.Network.registerForPushNotifications()` to get the device token.
450
444
 
451
- ### FCM Configuration (Android)
445
+ ### FCM configuration (Android)
452
446
 
453
- 1. Create a project in Firebase Console
454
- 2. Add your Android app (use your app's package name)
455
- 3. Download `google-services.json` and place in project root
456
- 4. Add the `titanium-firebase-cloud-messaging` module to tiapp.xml
457
- 5. Register for push and obtain the FCM token
447
+ 1. Create a project in Firebase Console.
448
+ 2. Add your Android app (use your app's package name).
449
+ 3. Download `google-services.json` and place it in the project root.
450
+ 4. Add `titanium-firebase-cloud-messaging` to `tiapp.xml`.
451
+ 5. Register for push and obtain the FCM token.
458
452
 
459
- ### Push Notification Payload Format
453
+ ### Push notification payload format
460
454
 
461
- **iOS:**
455
+ iOS:
462
456
  ```json
463
457
  {
464
- "aps": {
465
- "alert": "Message text",
466
- "badge": 1,
467
- "sound": "default"
468
- },
469
- "custom_field": "custom_value"
458
+ "aps": {
459
+ "alert": "Message text",
460
+ "badge": 1,
461
+ "sound": "default"
462
+ },
463
+ "custom_field": "custom_value"
470
464
  }
471
465
  ```
472
466
 
473
- **Android:**
467
+ Android:
474
468
  ```json
475
469
  {
476
- "message": "Message text",
477
- "title": "Notification Title",
478
- "custom_field": "custom_value"
470
+ "message": "Message text",
471
+ "title": "Notification Title",
472
+ "custom_field": "custom_value"
479
473
  }
480
474
  ```
481
475
 
482
- ### Send Push Notifications
476
+ ### Send push notifications
483
477
 
484
478
  Send push notifications from your server using the FCM HTTP API or APNs. Collect device tokens from client registration and target them with your push payload.
485
479
 
486
- ### Best Practices
480
+ ### Best practices
487
481
 
488
- 1. **Permissions**: iOS requires user permission (prompted on first call to `registerForPushNotifications`)
489
- 2. **Device Tokens**: Cache and send to your server; can change on app updates
490
- 3. **Background Handling**: Use proper activation modes for interactive notifications
491
- 4. **Badge Management**: Always reset badge after processing notification
492
- 5. **Error Handling**: Handle registration failures gracefully
493
- 6. **Testing**: Use development certificates/sandbox for testing iOS
482
+ 1. Permissions: iOS requires user permission (prompted on first call to `registerForPushNotifications`).
483
+ 2. Device tokens: cache and send to your server; they can change on app updates.
484
+ 3. Background handling: use proper activation modes for interactive notifications.
485
+ 4. Badge management: reset the badge after processing a notification.
486
+ 5. Error handling: handle registration failures gracefully.
487
+ 6. Testing: use development certificates/sandbox for iOS testing.
494
488
 
495
- ---
489
+ ## Common patterns
496
490
 
497
- ## Common Patterns
498
-
499
- ### Check Notification Permissions
491
+ ### Check notification permissions
500
492
 
501
493
  ```javascript
502
494
  // Android 4.1+ can disable notifications per app
503
495
  // Check via Settings or handle gracefully (notification may not be shown)
504
496
  ```
505
497
 
506
- ### PendingIntent for Notification Actions
498
+ ### PendingIntent for notification actions
507
499
 
508
500
  ```javascript
509
501
  const intent = Ti.Android.createIntent({
510
- action: 'com.example.MY_ACTION',
511
- // Add data/flags
502
+ action: 'com.example.MY_ACTION'
503
+ // Add data/flags
512
504
  });
513
505
  const pendingIntent = Ti.Android.createPendingIntent({
514
- intent: intent,
515
- flags: Ti.Android.FLAG_UPDATE_CURRENT
506
+ intent: intent,
507
+ flags: Ti.Android.FLAG_UPDATE_CURRENT
516
508
  });
517
509
  ```
518
510
 
519
- ### Notification Groups (Android 7.0+)
511
+ ### Notification groups (Android 7.0+)
520
512
 
521
513
  ```javascript
522
514
  // Requires API 24+; check documentation for latest implementation
523
515
  ```
524
516
 
525
- ### Rich Notifications (iOS 10+)
517
+ ### Rich notifications (iOS 10+)
526
518
 
527
519
  Use `UNMutableNotificationContent` with attachments (images, video, audio).
528
520
 
529
- ### Silent Push
521
+ ### Silent push
530
522
 
531
523
  ```javascript
532
524
  // iOS: include "content-available": 1 in payload
533
525
  // Android: use data messages (FCM)
534
526
  ```
535
527
 
536
- ---
537
-
538
- ## Platform-Specific Notes
528
+ ## Platform-specific notes
539
529
 
540
530
  ### Android
541
531
 
542
- - **Service lifecycle**: Services may be killed if app is swiped away. **Warning**: Android services may stop if the app is killed (user backs out or removes from recent apps). For critical notifications, use FCM push notifications instead of local services
543
- - **Notification channels**: Android 8.0+ requires notification channels
544
- - **Limitations**: Legacy MapView only supports single map view
532
+ - Service lifecycle: services may be killed if app is swiped away. For critical notifications, use FCM instead of local services.
533
+ - Notification channels: Android 8.0+ requires notification channels.
534
+ - Limitations: legacy MapView only supports a single map view.
545
535
 
546
536
  ### iOS
547
537
 
548
- - **Sandbox vs Production**: Use correct certificates and endpoints
549
- - **Background app refresh**: May be needed for certain push scenarios
550
- - **LiveView**: Callbacks only work properly when LiveView is enabled
538
+ - Sandbox vs production: use correct certificates and endpoints.
539
+ - Background app refresh: may be needed for certain push scenarios.
540
+ - LiveView: callbacks only work properly when LiveView is enabled.