@maccesar/titools 3.2.0 → 4.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.
Files changed (102) hide show
  1. package/README.md +27 -61
  2. package/lib/cleanup.js +29 -0
  3. package/lib/commands/agents.js +8 -7
  4. package/lib/commands/skills.js +118 -23
  5. package/lib/config.js +13 -15
  6. package/lib/installer.js +5 -3
  7. package/lib/utils.js +36 -0
  8. package/package.json +1 -1
  9. package/skills/alloy-guides/SKILL.md +188 -0
  10. package/skills/alloy-guides/references/CLI_TASKS.md +233 -0
  11. package/skills/alloy-guides/references/CONCEPTS.md +171 -0
  12. package/skills/alloy-guides/references/CONTROLLERS.md +279 -0
  13. package/skills/alloy-guides/references/MODELS.md +742 -0
  14. package/skills/alloy-guides/references/MODELS_ADVANCED.md +482 -0
  15. package/skills/alloy-guides/references/PURGETSS.md +57 -0
  16. package/skills/alloy-guides/references/VIEWS_DYNAMIC.md +235 -0
  17. package/skills/alloy-guides/references/VIEWS_STYLES.md +375 -0
  18. package/skills/alloy-guides/references/VIEWS_WITHOUT_CONTROLLERS.md +102 -0
  19. package/skills/alloy-guides/references/VIEWS_XML.md +581 -0
  20. package/skills/alloy-guides/references/WIDGETS.md +160 -0
  21. package/skills/alloy-howtos/SKILL.md +179 -0
  22. package/skills/alloy-howtos/references/best_practices.md +121 -0
  23. package/skills/alloy-howtos/references/cli_reference.md +230 -0
  24. package/skills/alloy-howtos/references/config_files.md +158 -0
  25. package/skills/alloy-howtos/references/custom_tags.md +148 -0
  26. package/skills/alloy-howtos/references/debugging_troubleshooting.md +78 -0
  27. package/skills/alloy-howtos/references/samples.md +156 -0
  28. package/skills/purgetss/SKILL.md +184 -181
  29. package/skills/purgetss/references/EXAMPLES.md +8 -6
  30. package/skills/purgetss/references/app-branding.md +5 -0
  31. package/skills/purgetss/references/apply-directive.md +1 -1
  32. package/skills/purgetss/references/class-categories.md +1 -1
  33. package/skills/purgetss/references/class-index-properties.md +618 -0
  34. package/skills/purgetss/references/class-index.md +4 -610
  35. package/skills/purgetss/references/cli-commands.md +41 -410
  36. package/skills/purgetss/references/custom-fonts.md +292 -0
  37. package/skills/purgetss/references/custom-rules.md +1 -1
  38. package/skills/purgetss/references/customization-deep-dive.md +64 -1
  39. package/skills/purgetss/references/icon-fonts.md +208 -118
  40. package/skills/purgetss/references/installation-setup.md +4 -4
  41. package/skills/purgetss/references/migration-guide.md +53 -1
  42. package/skills/purgetss/references/multi-density-images.md +53 -2
  43. package/skills/purgetss/references/svg-pipeline.md +249 -0
  44. package/skills/purgetss/references/tikit-components.md +2 -2
  45. package/skills/purgetss/references/titanium-resets.md +2 -2
  46. package/skills/purgetss/references/ui-ux-design.md +7 -5
  47. package/skills/purgetss/references/version-history.md +71 -82
  48. package/skills/ti-api/SKILL.md +128 -0
  49. package/skills/ti-api/references/api-android.md +675 -0
  50. package/skills/ti-api/references/api-app-platform.md +636 -0
  51. package/skills/ti-api/references/api-core.md +764 -0
  52. package/skills/ti-api/references/api-data-network.md +641 -0
  53. package/skills/ti-api/references/api-media.md +655 -0
  54. package/skills/ti-api/references/api-modules-ble-bluetooth.md +657 -0
  55. package/skills/ti-api/references/api-modules-coremotion-urlsession.md +411 -0
  56. package/skills/ti-api/references/api-modules-map.md +632 -0
  57. package/skills/ti-api/references/api-modules-nfc.md +725 -0
  58. package/skills/ti-api/references/api-modules-social-misc.md +526 -0
  59. package/skills/ti-api/references/api-services.md +700 -0
  60. package/skills/ti-api/references/api-ui-android.md +499 -0
  61. package/skills/ti-api/references/api-ui-extras.md +702 -0
  62. package/skills/ti-api/references/api-ui-ios-animator.md +378 -0
  63. package/skills/ti-api/references/api-ui-ios.md +756 -0
  64. package/skills/ti-api/references/api-ui-lists.md +581 -0
  65. package/skills/ti-api/references/api-ui-text-input.md +607 -0
  66. package/skills/ti-api/references/api-ui-views.md +572 -0
  67. package/skills/ti-api/references/api-ui-windows-navigation.md +676 -0
  68. package/skills/ti-api/references/api-xml-global.md +743 -0
  69. package/skills/ti-guides/SKILL.md +82 -0
  70. package/skills/ti-guides/references/advanced-data-and-images.md +155 -0
  71. package/skills/ti-guides/references/android-manifest.md +97 -0
  72. package/skills/ti-guides/references/app-distribution.md +373 -0
  73. package/skills/ti-guides/references/application-frameworks.md +366 -0
  74. package/skills/ti-guides/references/cli-reference.md +700 -0
  75. package/skills/ti-guides/references/coding-best-practices.md +150 -0
  76. package/skills/ti-guides/references/commonjs-advanced.md +279 -0
  77. package/skills/ti-guides/references/hello-world.md +99 -0
  78. package/skills/ti-guides/references/hyperloop-native-access.md +458 -0
  79. package/skills/ti-guides/references/javascript-primer.md +402 -0
  80. package/skills/ti-guides/references/reserved-words.md +36 -0
  81. package/skills/ti-guides/references/resources.md +172 -0
  82. package/skills/ti-guides/references/style-and-conventions.md +104 -0
  83. package/skills/ti-guides/references/tiapp-config.md +655 -0
  84. package/skills/ti-howtos/SKILL.md +131 -0
  85. package/skills/ti-howtos/references/android-platform-deep-dives.md +609 -0
  86. package/skills/ti-howtos/references/automation-fastlane-appium.md +96 -0
  87. package/skills/ti-howtos/references/buffer-codec-streams.md +162 -0
  88. package/skills/ti-howtos/references/cross-platform-development.md +358 -0
  89. package/skills/ti-howtos/references/debugging-profiling.md +473 -0
  90. package/skills/ti-howtos/references/extending-titanium.md +684 -0
  91. package/skills/ti-howtos/references/google-maps-v2.md +172 -0
  92. package/skills/ti-howtos/references/ios-map-kit.md +149 -0
  93. package/skills/ti-howtos/references/ios-platform-deep-dives.md +595 -0
  94. package/skills/ti-howtos/references/local-data-sources.md +310 -0
  95. package/skills/ti-howtos/references/location-and-maps.md +267 -0
  96. package/skills/ti-howtos/references/media-apis.md +268 -0
  97. package/skills/ti-howtos/references/notification-services.md +539 -0
  98. package/skills/ti-howtos/references/remote-data-sources.md +339 -0
  99. package/skills/ti-howtos/references/tutorials.md +552 -0
  100. package/skills/ti-howtos/references/using-modules.md +182 -0
  101. package/skills/ti-howtos/references/web-content-integration.md +288 -0
  102. package/skills/ti-howtos/references/webpack-build-pipeline.md +125 -0
@@ -0,0 +1,310 @@
1
+ # Local data sources
2
+
3
+ ## 1. Filesystem access and storage
4
+
5
+ ### Modules overview
6
+ - `Ti.Filesystem` - top-level module for file/directory operations
7
+ - `Ti.Filesystem.File` - file object with read/write methods
8
+ - `Ti.Filesystem.FileStream` - stream wrapper implementing `Ti.IOStream`
9
+
10
+ ### Storage locations
11
+
12
+ | Location | Read/Write | Persistence | Notes |
13
+ | --------------------------- | ---------- | ---------------- | -------------------------------------- |
14
+ | `applicationDataDirectory` | R/W | Until uninstall | Primary app data location |
15
+ | `resourcesDirectory` | R-only | Until uninstall | App resources (R/W in simulator only) |
16
+ | `tempDirectory` | R/W | Until app closes | OS may delete anytime |
17
+ | `applicationCacheDirectory` | R/W | OS may clean | For cached data |
18
+ | `externalCacheDirectory` | R/W | OS may clean | Android SD card cache |
19
+ | `externalStorageDirectory` | R/W | Until uninstall | Android SD card storage |
20
+
21
+ Always check `Ti.Filesystem.isExternalStoragePresent()` before using external storage on Android.
22
+
23
+ ### File operations
24
+
25
+ #### Getting a file handle
26
+ ```javascript
27
+ const f = Ti.Filesystem.getFile(
28
+ Ti.Filesystem.applicationDataDirectory,
29
+ 'myfile.txt'
30
+ );
31
+ ```
32
+
33
+ #### Writing files
34
+ ```javascript
35
+ f.write('Content here'); // Overwrites or creates
36
+ ```
37
+
38
+ #### Reading files
39
+ ```javascript
40
+ const contents = f.read(); // Returns Blob
41
+ Ti.API.info(contents.text); // Text content
42
+ Ti.API.info(contents.mimeType); // MIME type
43
+ ```
44
+
45
+ #### Appending
46
+ ```javascript
47
+ f.append('More content\n'); // String, Blob, or File
48
+ ```
49
+
50
+ #### Creating/copying
51
+ ```javascript
52
+ // Auto-creates on write, but explicit option exists:
53
+ if (!f.exists()) {
54
+ f.createFile();
55
+ }
56
+
57
+ // Android copy method:
58
+ const oldFile = Ti.Filesystem.getFile(applicationDataDirectory, 'old.txt');
59
+ oldFile.copy(`${applicationDataDirectory}new.txt`);
60
+
61
+ // iOS (no copy method):
62
+ const newFile = Ti.Filesystem.getFile(applicationDataDirectory, 'new.txt');
63
+ newFile.write(oldFile.read());
64
+ ```
65
+
66
+ #### Renaming
67
+ Important: the file handle still points to the old name after rename.
68
+
69
+ ```javascript
70
+ const f = Ti.Filesystem.getFile(applicationDataDirectory, 'old.txt');
71
+ f.rename('new.txt');
72
+ // f still references 'old.txt' (now non-existent)
73
+ const newf = Ti.Filesystem.getFile(applicationDataDirectory, 'new.txt');
74
+ ```
75
+
76
+ #### Deleting
77
+ ```javascript
78
+ if (f.exists() && f.writable) {
79
+ const success = f.deleteFile(); // Returns Boolean, no error thrown
80
+ }
81
+ ```
82
+
83
+ ### Directory operations
84
+
85
+ ```javascript
86
+ // Create directory
87
+ const dir = Ti.Filesystem.getFile(applicationDataDirectory, 'mysubdir');
88
+ dir.createDirectory();
89
+
90
+ // List contents
91
+ const listing = dir.getDirectoryListing();
92
+
93
+ // Move file into directory
94
+ const file = Ti.Filesystem.getFile(applicationDataDirectory, 'file.txt');
95
+ file.move('mysubdir/file.txt');
96
+
97
+ // Delete directory (must be empty or force recursive)
98
+ dir.deleteDirectory(false); // Fails if not empty
99
+ dir.deleteDirectory(true); // Recursive delete
100
+ ```
101
+
102
+ ### Case sensitivity warning
103
+ Android and Mobile Web use case-sensitive filesystems. File names referenced in code must match actual file names exactly. Recommendation: use lowercase file names.
104
+
105
+ ## 2. SQLite database
106
+
107
+ ### Installing a pre-populated database
108
+ Ship a database with your app and install it on first launch:
109
+ ```javascript
110
+ // Copies db from Resources/ (or app/assets/) to applicationDataDirectory
111
+ const db = Ti.Database.install('seeds/mydata.sqlite', 'mydata');
112
+ // On subsequent launches, install() just opens the existing database
113
+ ```
114
+
115
+ ### Opening databases
116
+
117
+ ```javascript
118
+ // Install (copies from Resources if first time)
119
+ const db = Ti.Database.install('mydb.sqlite', 'myInstalledDB');
120
+
121
+ // Or open existing database in applicationDataDirectory
122
+ const db = Ti.Database.open('myDatabase');
123
+
124
+ // For app data directory location:
125
+ const dbFile = Ti.Filesystem.getFile(
126
+ Ti.Filesystem.applicationDataDirectory,
127
+ 'mydb.sqlite'
128
+ );
129
+ const db = Ti.Database.open(dbFile.nativePath);
130
+ ```
131
+
132
+ ### Querying data
133
+
134
+ ```javascript
135
+ const rows = db.execute('SELECT * FROM users WHERE age > ?', [18]);
136
+
137
+ while (rows.isValidRow()) {
138
+ Ti.API.info(rows.fieldByName('name'));
139
+ Ti.API.info(rows.field(0)); // First column by index
140
+ rows.next();
141
+ }
142
+
143
+ // Always close result set
144
+ rows.close();
145
+ ```
146
+
147
+ ### Parameterized queries
148
+ Always use parameters to prevent SQL injection:
149
+
150
+ ```javascript
151
+ // Safe - parameterized
152
+ db.execute('INSERT INTO users (name, age) VALUES (?, ?)', ['John', 25]);
153
+
154
+ // Unsafe - never do this
155
+ db.execute(`INSERT INTO users (name, age) VALUES ('${name}', ${age})`);
156
+ ```
157
+
158
+ ### Data modification
159
+
160
+ ```javascript
161
+ // INSERT
162
+ db.execute('INSERT INTO users (name, age) VALUES (?, ?)', ['Jane', 30]);
163
+ const lastId = db.lastInsertRowId; // ID of the last inserted row
164
+ const affected = db.rowsAffected; // number of rows changed by last statement
165
+
166
+ // UPDATE
167
+ db.execute('UPDATE users SET age = ? WHERE name = ?', [31, 'Jane']);
168
+ const rowsAffected = db.rowsAffected;
169
+
170
+ // DELETE
171
+ db.execute('DELETE FROM users WHERE age < ?', [18]);
172
+ ```
173
+
174
+ ### Transactions for batch operations
175
+ Use transactions to speed up multiple inserts or updates:
176
+ ```javascript
177
+ const db = Ti.Database.open('mydb');
178
+ db.execute('BEGIN');
179
+ try {
180
+ for (let i = 0; i < items.length; i++) {
181
+ db.execute('INSERT INTO products (name, price) VALUES (?, ?)', items[i].name, items[i].price);
182
+ }
183
+ db.execute('COMMIT');
184
+ } catch (e) {
185
+ db.execute('ROLLBACK');
186
+ Ti.API.error(`Transaction failed: ${e.message}`);
187
+ }
188
+ db.close();
189
+ ```
190
+
191
+ Performance: wrapping 1000 inserts in a transaction can be 10-100x faster than individual inserts.
192
+
193
+ ### SQLite limitations
194
+ - No `FULL OUTER JOIN` support. Use `LEFT JOIN` with `UNION`.
195
+ - Limited `ALTER TABLE`. You can only `ADD COLUMN` or `RENAME TABLE`.
196
+ - No built-in referential integrity (foreign keys). Enable with `PRAGMA foreign_keys = ON`.
197
+ - No native boolean type. Use INTEGER (0/1).
198
+
199
+ ### Disable iCloud backup for databases (iOS)
200
+
201
+ iOS backs up databases to iCloud by default. For large or recreatable databases, disable this:
202
+ ```javascript
203
+ const db = Ti.Database.open('mydb');
204
+ db.file.setRemoteBackup(false);
205
+ db.close();
206
+ ```
207
+
208
+ Important: Apple may reject apps that back up large recreatable data to iCloud.
209
+
210
+ ### Always close connections
211
+
212
+ ```javascript
213
+ try {
214
+ // Database operations
215
+ } finally {
216
+ // Always close, even on error
217
+ if (rows) rows.close();
218
+ if (db) db.close();
219
+ }
220
+ ```
221
+
222
+ ## 3. Properties API (Ti.App.Properties)
223
+
224
+ ### Overview
225
+ Lightweight key-value storage for simple data types. Loaded into memory at launch for fast access.
226
+
227
+ Warning: there is no hard limit, but all properties load into memory. Avoid storing large data.
228
+
229
+ ### Data type methods
230
+
231
+ | Type | Get method | Set method |
232
+ | ------ | -------------------------- | ------------------------ |
233
+ | String | `getString(name, default)` | `setString(name, value)` |
234
+ | Int | `getInt(name, default)` | `setInt(name, value)` |
235
+ | Double | `getDouble(name, default)` | `setDouble(name, value)` |
236
+ | Bool | `getBool(name, default)` | `setBool(name, value)` |
237
+ | List | `getList(name, default)` | `setList(name, value)` |
238
+
239
+ ### Usage examples
240
+
241
+ ```javascript
242
+ // Set values
243
+ Ti.App.Properties.setString('username', 'john');
244
+ Ti.App.Properties.setInt('loginCount', 5);
245
+ Ti.App.Properties.setBool('isLoggedIn', true);
246
+ Ti.App.Properties.setDouble('price', 19.99);
247
+ Ti.App.Properties.setList('favorites', ['item1', 'item2']);
248
+
249
+ // Get values (with defaults)
250
+ const username = Ti.App.Properties.getString('username', 'guest');
251
+ const count = Ti.App.Properties.getInt('loginCount', 0);
252
+
253
+ // Check if property exists
254
+ if (Ti.App.Properties.hasProperty('username')) {
255
+ // ...
256
+ }
257
+
258
+ // Remove property
259
+ Ti.App.Properties.removeProperty('username');
260
+
261
+ // List all properties
262
+ const allProps = Ti.App.Properties.listProperties();
263
+ ```
264
+
265
+ ### Storing complex objects as JSON
266
+
267
+ ```javascript
268
+ // Store object as JSON string
269
+ const data = { city: 'Mountain View', temp: 72 };
270
+ Ti.App.Properties.setString('weatherData', JSON.stringify(data));
271
+
272
+ // Retrieve and parse
273
+ const stored = Ti.App.Properties.getString('weatherData', '{}');
274
+ const weather = JSON.parse(stored);
275
+ Ti.API.info(weather.city); // 'Mountain View'
276
+ ```
277
+
278
+ ### Platform storage
279
+ - iOS: `NSUserDefaults` in a `.plist` file
280
+ - Android: XML file at `/data/data/com.domain.app/shared_prefs/titanium.xml`
281
+
282
+ ## 4. Advanced data manipulation (Buffer, Codec, and Streams)
283
+
284
+ For binary data handling, character encoding, and large file streams, see:
285
+
286
+ - Buffer, Codec, and Streams: `buffer-codec-streams.md`
287
+
288
+ ## 5. Choosing a persistence strategy
289
+
290
+ ### Decision guide
291
+
292
+ | Scenario | Recommended approach |
293
+ | ---------------------------------- | ---------------------------------------- |
294
+ | User settings/preferences | `Ti.App.Properties` |
295
+ | Small config data (< 100KB) | `Ti.App.Properties` with JSON |
296
+ | Structured relational data | SQLite |
297
+ | Large binary data (images, videos) | Filesystem |
298
+ | Downloaded content cache | Filesystem (`applicationCacheDirectory`) |
299
+ | Temporary processing data | Filesystem (`tempDirectory`) |
300
+ | User-generated files | Filesystem (`applicationDataDirectory`) |
301
+ | Offline-first app data | SQLite + Filesystem combo |
302
+
303
+ ### Best practices
304
+
305
+ 1. Properties API: use only for small, frequently accessed config data.
306
+ 2. SQLite: always close connections and result sets; use parameterized queries.
307
+ 3. Filesystem: check external storage availability on Android; handle case sensitivity.
308
+ 4. Streams: use for large file operations to avoid memory issues.
309
+ 5. Hybrid approach: store metadata in SQLite, file paths in records, actual files on filesystem.
310
+ 6. Cleanup: implement cleanup for temp files and cache; do not let them accumulate.
@@ -0,0 +1,267 @@
1
+ # Location services and maps
2
+
3
+ ## 1. GPS position tracking
4
+
5
+ ### FusedLocationProvider (Android)
6
+ Since TiSDK 7.1.0, Titanium supports `FusedLocationProvider` for significant battery savings.
7
+ Requirement: include the `ti.playservices` module in your project.
8
+
9
+ ### Accuracy configuration
10
+
11
+ iOS accuracy levels:
12
+ - `ACCURACY_BEST` - highest power (GPS)
13
+ - `ACCURACY_NEAREST_TEN_METERS` - medium-high power
14
+ - `ACCURACY_HUNDRED_METERS` - medium power
15
+ - `ACCURACY_KILOMETER` - low power
16
+ - `ACCURACY_THREE_KILOMETERS` - lowest power (cell/wifi)
17
+
18
+ Key properties:
19
+ ```javascript
20
+ Ti.Geolocation.accuracy = Ti.Geolocation.ACCURACY_BEST;
21
+ Ti.Geolocation.distanceFilter = 10; // meters - only fire if moved X meters
22
+ Ti.Geolocation.headingFilter = 5; // degrees - only fire if heading changed X degrees
23
+ Ti.Geolocation.preferredProvider = Ti.Geolocation.PROVIDER_GPS; // or PROVIDER_NETWORK
24
+ ```
25
+
26
+ Note: on many Android devices, a low-precision passive location provider is always enabled, even when the user disables GPS and Network providers. `Ti.Geolocation.locationServicesEnabled` may always return `true` on those devices.
27
+
28
+ ### Android configuration modes
29
+
30
+ Simple mode (ACCURACY_HIGH/LOW):
31
+ ```javascript
32
+ Ti.Geolocation.accuracy = Ti.Geolocation.ACCURACY_HIGH;
33
+ ```
34
+
35
+ Manual mode (fine-grained control):
36
+ ```javascript
37
+ const providerGps = Ti.Geolocation.Android.createLocationProvider({
38
+ name: Ti.Geolocation.PROVIDER_GPS,
39
+ minUpdateDistance: 0.0,
40
+ minUpdateTime: 0
41
+ });
42
+ Ti.Geolocation.Android.addLocationProvider(providerGps);
43
+ Ti.Geolocation.Android.manualMode = true;
44
+ ```
45
+
46
+ ### Location permissions
47
+
48
+ ```javascript
49
+ // Check permissions
50
+ if (Ti.Geolocation.hasLocationPermissions(Ti.Geolocation.AUTHORIZATION_WHEN_IN_USE)) {
51
+ startTracking();
52
+ } else {
53
+ Ti.Geolocation.requestLocationPermissions(Ti.Geolocation.AUTHORIZATION_WHEN_IN_USE, (e) => {
54
+ if (e.success) {
55
+ startTracking();
56
+ } else {
57
+ Ti.API.error(`Permission denied: ${e.error}`);
58
+ }
59
+ });
60
+ }
61
+ ```
62
+
63
+ iOS plist keys (tiapp.xml):
64
+ ```xml
65
+ <key>NSLocationWhenInUseUsageDescription</key>
66
+ <string>We need your location to show nearby places</string>
67
+ <!-- For background location: -->
68
+ <key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
69
+ <string>We need background location for navigation</string>
70
+ ```
71
+
72
+ Note (iOS 11+): you must include `NSLocationAlwaysAndWhenInUseUsageDescription` even when requesting "Always" permission. Users can still choose "When in Use".
73
+
74
+ ### One-time position
75
+
76
+ ```javascript
77
+ if (Ti.Geolocation.locationServicesEnabled) {
78
+ Ti.Geolocation.getCurrentPosition((e) => {
79
+ if (e.error) {
80
+ Ti.API.error(`Error: ${e.error}`);
81
+ } else {
82
+ const coords = e.coords;
83
+ // { latitude, longitude, altitude, accuracy, heading, speed, timestamp }
84
+ }
85
+ });
86
+ }
87
+ ```
88
+
89
+ ### Continuous monitoring
90
+
91
+ ```javascript
92
+ Ti.Geolocation.addEventListener('location', (e) => {
93
+ if (e.error) {
94
+ Ti.API.error(`Error: ${e.error}`);
95
+ } else {
96
+ Ti.API.info(`${e.coords.latitude}, ${e.coords.longitude}`);
97
+ }
98
+ });
99
+ ```
100
+
101
+ ### Lifecycle management (critical for battery)
102
+
103
+ Android Activity events:
104
+ ```javascript
105
+ let locationAdded = false;
106
+ const handleLocation = (e) => {
107
+ if (!e.error) {
108
+ Ti.API.info(e.coords);
109
+ }
110
+ };
111
+
112
+ function addHandler() {
113
+ if (!locationAdded) {
114
+ Ti.Geolocation.addEventListener('location', handleLocation);
115
+ locationAdded = true;
116
+ }
117
+ }
118
+
119
+ function removeHandler() {
120
+ if (locationAdded) {
121
+ Ti.Geolocation.removeEventListener('location', handleLocation);
122
+ locationAdded = false;
123
+ }
124
+ }
125
+
126
+ // Add listeners
127
+ addHandler();
128
+
129
+ const activity = Ti.Android.currentActivity;
130
+ activity.addEventListener('destroy', removeHandler);
131
+ activity.addEventListener('pause', removeHandler); // Stop when in background
132
+ activity.addEventListener('resume', addHandler); // Resume when foreground
133
+ ```
134
+
135
+ ### Compass/heading
136
+
137
+ ```javascript
138
+ // One-time heading
139
+ Ti.Geolocation.getCurrentHeading((e) => {
140
+ Ti.API.info(e.heading.magneticHeading); // degrees from magnetic north
141
+ Ti.API.info(e.heading.trueHeading); // degrees from true north
142
+ });
143
+
144
+ // Continuous heading monitoring
145
+ Ti.Geolocation.addEventListener('heading', (e) => {
146
+ if (!e.error) {
147
+ Ti.API.info(`Heading: ${e.heading.magneticHeading}`);
148
+ }
149
+ });
150
+ ```
151
+
152
+ The heading event object includes:
153
+ - `heading.magneticHeading` - degrees relative to magnetic north
154
+ - `heading.trueHeading` - degrees relative to true north (requires location)
155
+ - `heading.accuracy` - deviation in degrees (lower is better)
156
+ - `heading.x`, `heading.y`, `heading.z` - raw magnetometer data (microteslas)
157
+
158
+ ## Geocoding
159
+
160
+ ### Forward geocoding (address → coordinates)
161
+
162
+ ```javascript
163
+ Ti.Geolocation.forwardGeocoder('440 Bernardo Ave Mountain View CA', (e) => {
164
+ if (e.success) {
165
+ Ti.API.info(`Lat: ${e.latitude}, Lon: ${e.longitude}`);
166
+ }
167
+ });
168
+ ```
169
+
170
+ With error handling:
171
+ ```javascript
172
+ Ti.Geolocation.forwardGeocoder('1600 Amphitheatre Pkwy, Mountain View, CA', (e) => {
173
+ if (!e.success || e.error) {
174
+ Ti.API.error(`Geocoding failed: ${e.error}`);
175
+ return;
176
+ }
177
+ Ti.API.info(`Lat: ${e.latitude}, Lng: ${e.longitude}`);
178
+ });
179
+ ```
180
+
181
+ ### Reverse geocoding (coordinates → places)
182
+
183
+ ```javascript
184
+ Ti.Geolocation.reverseGeocoder(37.389569, -122.050212, (e) => {
185
+ if (e.success) {
186
+ e.places.forEach((place) => {
187
+ Ti.API.info(place.address); // Full address
188
+ Ti.API.info(place.city); // City name
189
+ Ti.API.info(place.country); // Country
190
+ Ti.API.info(place.zipcode); // Postal code
191
+ });
192
+ }
193
+ });
194
+ ```
195
+
196
+ ## 2. Native maps (platform-specific details)
197
+
198
+ Due to the complexity of modern native maps, refer to the detailed guides:
199
+
200
+ - Google Maps v2 for Android: `google-maps-v2.md`
201
+ - iOS Map Kit: `ios-map-kit.md`
202
+
203
+ ## Common map patterns
204
+
205
+ ### Add annotation after map creation
206
+
207
+ ```javascript
208
+ mapview.addAnnotation(annotation);
209
+ ```
210
+
211
+ ### Remove annotation
212
+
213
+ ```javascript
214
+ mapview.removeAnnotation(annotation);
215
+ ```
216
+
217
+ ### Remove all annotations
218
+
219
+ ```javascript
220
+ mapview.removeAllAnnotations();
221
+ ```
222
+
223
+ ### Select annotation programmatically
224
+
225
+ ```javascript
226
+ mapview.selectAnnotation(annotation);
227
+ ```
228
+
229
+ ### Deselect annotation
230
+
231
+ ```javascript
232
+ mapview.deselectAnnotation(annotation);
233
+ ```
234
+
235
+ ### Set region with animation
236
+
237
+ ```javascript
238
+ mapview.setLocation({
239
+ latitude: 37.389569,
240
+ longitude: -122.050212,
241
+ animate: true
242
+ });
243
+
244
+ // Or for region
245
+ mapview.setRegion({
246
+ latitude: 37.389569,
247
+ longitude: -122.050212,
248
+ latitudeDelta: 0.05,
249
+ longitudeDelta: 0.05,
250
+ animate: true
251
+ });
252
+ ```
253
+
254
+ ### Zoom to annotations
255
+
256
+ ```javascript
257
+ mapview.showAnnotations([annotation1, annotation2], true); // true = animate
258
+ ```
259
+
260
+ ### Best practices
261
+
262
+ 1. Battery life: remove location listeners when not needed. Use `distanceFilter` to reduce updates.
263
+ 2. Accuracy: use the lowest accuracy that meets your needs.
264
+ 3. Permissions: check permissions before requesting location. Handle denial gracefully.
265
+ 4. Maps on Android: only one map view per application (legacy). Use `ti.map` for multiple views.
266
+ 5. API keys: Google Maps API key required for Android (debug and production).
267
+ 6. User location: requires location permissions (`NSLocationWhenInUseUsageDescription` for iOS).