@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.
- package/README.md +27 -61
- package/lib/cleanup.js +29 -0
- package/lib/commands/agents.js +8 -7
- package/lib/commands/skills.js +118 -23
- package/lib/config.js +13 -15
- package/lib/installer.js +5 -3
- package/lib/utils.js +36 -0
- package/package.json +1 -1
- package/skills/alloy-guides/SKILL.md +188 -0
- package/skills/alloy-guides/references/CLI_TASKS.md +233 -0
- package/skills/alloy-guides/references/CONCEPTS.md +171 -0
- package/skills/alloy-guides/references/CONTROLLERS.md +279 -0
- package/skills/alloy-guides/references/MODELS.md +742 -0
- package/skills/alloy-guides/references/MODELS_ADVANCED.md +482 -0
- package/skills/alloy-guides/references/PURGETSS.md +57 -0
- package/skills/alloy-guides/references/VIEWS_DYNAMIC.md +235 -0
- package/skills/alloy-guides/references/VIEWS_STYLES.md +375 -0
- package/skills/alloy-guides/references/VIEWS_WITHOUT_CONTROLLERS.md +102 -0
- package/skills/alloy-guides/references/VIEWS_XML.md +581 -0
- package/skills/alloy-guides/references/WIDGETS.md +160 -0
- package/skills/alloy-howtos/SKILL.md +179 -0
- package/skills/alloy-howtos/references/best_practices.md +121 -0
- package/skills/alloy-howtos/references/cli_reference.md +230 -0
- package/skills/alloy-howtos/references/config_files.md +158 -0
- package/skills/alloy-howtos/references/custom_tags.md +148 -0
- package/skills/alloy-howtos/references/debugging_troubleshooting.md +78 -0
- package/skills/alloy-howtos/references/samples.md +156 -0
- package/skills/purgetss/SKILL.md +184 -181
- package/skills/purgetss/references/EXAMPLES.md +8 -6
- package/skills/purgetss/references/app-branding.md +5 -0
- package/skills/purgetss/references/apply-directive.md +1 -1
- package/skills/purgetss/references/class-categories.md +1 -1
- package/skills/purgetss/references/class-index-properties.md +618 -0
- package/skills/purgetss/references/class-index.md +4 -610
- package/skills/purgetss/references/cli-commands.md +41 -410
- package/skills/purgetss/references/custom-fonts.md +292 -0
- package/skills/purgetss/references/custom-rules.md +1 -1
- package/skills/purgetss/references/customization-deep-dive.md +64 -1
- package/skills/purgetss/references/icon-fonts.md +208 -118
- package/skills/purgetss/references/installation-setup.md +4 -4
- package/skills/purgetss/references/migration-guide.md +53 -1
- package/skills/purgetss/references/multi-density-images.md +53 -2
- package/skills/purgetss/references/svg-pipeline.md +249 -0
- package/skills/purgetss/references/tikit-components.md +2 -2
- package/skills/purgetss/references/titanium-resets.md +2 -2
- package/skills/purgetss/references/ui-ux-design.md +7 -5
- package/skills/purgetss/references/version-history.md +71 -82
- package/skills/ti-api/SKILL.md +128 -0
- package/skills/ti-api/references/api-android.md +675 -0
- package/skills/ti-api/references/api-app-platform.md +636 -0
- package/skills/ti-api/references/api-core.md +764 -0
- package/skills/ti-api/references/api-data-network.md +641 -0
- package/skills/ti-api/references/api-media.md +655 -0
- package/skills/ti-api/references/api-modules-ble-bluetooth.md +657 -0
- package/skills/ti-api/references/api-modules-coremotion-urlsession.md +411 -0
- package/skills/ti-api/references/api-modules-map.md +632 -0
- package/skills/ti-api/references/api-modules-nfc.md +725 -0
- package/skills/ti-api/references/api-modules-social-misc.md +526 -0
- package/skills/ti-api/references/api-services.md +700 -0
- package/skills/ti-api/references/api-ui-android.md +499 -0
- package/skills/ti-api/references/api-ui-extras.md +702 -0
- package/skills/ti-api/references/api-ui-ios-animator.md +378 -0
- package/skills/ti-api/references/api-ui-ios.md +756 -0
- package/skills/ti-api/references/api-ui-lists.md +581 -0
- package/skills/ti-api/references/api-ui-text-input.md +607 -0
- package/skills/ti-api/references/api-ui-views.md +572 -0
- package/skills/ti-api/references/api-ui-windows-navigation.md +676 -0
- package/skills/ti-api/references/api-xml-global.md +743 -0
- package/skills/ti-guides/SKILL.md +82 -0
- package/skills/ti-guides/references/advanced-data-and-images.md +155 -0
- package/skills/ti-guides/references/android-manifest.md +97 -0
- package/skills/ti-guides/references/app-distribution.md +373 -0
- package/skills/ti-guides/references/application-frameworks.md +366 -0
- package/skills/ti-guides/references/cli-reference.md +700 -0
- package/skills/ti-guides/references/coding-best-practices.md +150 -0
- package/skills/ti-guides/references/commonjs-advanced.md +279 -0
- package/skills/ti-guides/references/hello-world.md +99 -0
- package/skills/ti-guides/references/hyperloop-native-access.md +458 -0
- package/skills/ti-guides/references/javascript-primer.md +402 -0
- package/skills/ti-guides/references/reserved-words.md +36 -0
- package/skills/ti-guides/references/resources.md +172 -0
- package/skills/ti-guides/references/style-and-conventions.md +104 -0
- package/skills/ti-guides/references/tiapp-config.md +655 -0
- package/skills/ti-howtos/SKILL.md +131 -0
- package/skills/ti-howtos/references/android-platform-deep-dives.md +609 -0
- package/skills/ti-howtos/references/automation-fastlane-appium.md +96 -0
- package/skills/ti-howtos/references/buffer-codec-streams.md +162 -0
- package/skills/ti-howtos/references/cross-platform-development.md +358 -0
- package/skills/ti-howtos/references/debugging-profiling.md +473 -0
- package/skills/ti-howtos/references/extending-titanium.md +684 -0
- package/skills/ti-howtos/references/google-maps-v2.md +172 -0
- package/skills/ti-howtos/references/ios-map-kit.md +149 -0
- package/skills/ti-howtos/references/ios-platform-deep-dives.md +595 -0
- package/skills/ti-howtos/references/local-data-sources.md +310 -0
- package/skills/ti-howtos/references/location-and-maps.md +267 -0
- package/skills/ti-howtos/references/media-apis.md +268 -0
- package/skills/ti-howtos/references/notification-services.md +539 -0
- package/skills/ti-howtos/references/remote-data-sources.md +339 -0
- package/skills/ti-howtos/references/tutorials.md +552 -0
- package/skills/ti-howtos/references/using-modules.md +182 -0
- package/skills/ti-howtos/references/web-content-integration.md +288 -0
- package/skills/ti-howtos/references/webpack-build-pipeline.md +125 -0
|
@@ -0,0 +1,595 @@
|
|
|
1
|
+
# iOS platform deep dives
|
|
2
|
+
|
|
3
|
+
## 1. iOS 17+ privacy requirements (critical)
|
|
4
|
+
Apple requires declaring the use of certain APIs to prevent fingerprinting.
|
|
5
|
+
|
|
6
|
+
### PrivacyInfo.xcprivacy file
|
|
7
|
+
Create this file in `app/assets/iphone/` (Alloy) or `Resources/iphone/` (Classic):
|
|
8
|
+
```xml
|
|
9
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
10
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
11
|
+
<plist version="1.0">
|
|
12
|
+
<dict>
|
|
13
|
+
<key>NSPrivacyAccessedAPITypes</key>
|
|
14
|
+
<array>
|
|
15
|
+
<dict>
|
|
16
|
+
<key>NSPrivacyAccessedAPIType</key>
|
|
17
|
+
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
|
|
18
|
+
<key>NSPrivacyAccessedAPITypeReasons</key>
|
|
19
|
+
<array><string>AC6B.1</string></array>
|
|
20
|
+
</dict>
|
|
21
|
+
</array>
|
|
22
|
+
</dict>
|
|
23
|
+
</plist>
|
|
24
|
+
```
|
|
25
|
+
Common categories: `UserDefaults` (Ti.App.Properties), `FileTimestamp` (file.createdAt), `SystemBootTime`.
|
|
26
|
+
|
|
27
|
+
## 2. Background services and silent push
|
|
28
|
+
|
|
29
|
+
### Overview
|
|
30
|
+
iOS allows limited background execution. For large downloads, use the `com.titaniumsdk.urlSession` module.
|
|
31
|
+
|
|
32
|
+
### Silent push (background update)
|
|
33
|
+
Wakes up the app to download content without showing a notification.
|
|
34
|
+
|
|
35
|
+
`tiapp.xml`:
|
|
36
|
+
```xml
|
|
37
|
+
<key>UIBackgroundModes</key>
|
|
38
|
+
<array>
|
|
39
|
+
<string>remote-notification</string>
|
|
40
|
+
</array>
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
`app.js`:
|
|
44
|
+
```javascript
|
|
45
|
+
Ti.App.iOS.addEventListener('silentpush', (e) => {
|
|
46
|
+
// Start download or update
|
|
47
|
+
Ti.API.info(`Data received: ${JSON.stringify(e)}`);
|
|
48
|
+
|
|
49
|
+
// Mandatory to call upon completion (max 30 seconds)
|
|
50
|
+
Ti.App.iOS.endBackgroundHandler(e.handlerId);
|
|
51
|
+
});
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Background fetch
|
|
55
|
+
```javascript
|
|
56
|
+
// Enable in tiapp.xml:
|
|
57
|
+
// <key>UIBackgroundModes</key>
|
|
58
|
+
// <array><string>fetch</string></array>
|
|
59
|
+
|
|
60
|
+
// Set minimum fetch interval
|
|
61
|
+
Ti.App.iOS.setMinimumBackgroundFetchInterval(Ti.App.iOS.BACKGROUNDFETCHINTERVAL_MIN);
|
|
62
|
+
|
|
63
|
+
Ti.App.iOS.addEventListener('backgroundfetch', (e) => {
|
|
64
|
+
// Fetch new data
|
|
65
|
+
Ti.API.info('Background fetch triggered');
|
|
66
|
+
|
|
67
|
+
// Must call endBackgroundHandler when done (max 30 seconds)
|
|
68
|
+
Ti.App.iOS.endBackgroundHandler(e.handlerId);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
// To disable background fetch:
|
|
72
|
+
// Ti.App.iOS.setMinimumBackgroundFetchInterval(Ti.App.iOS.BACKGROUNDFETCHINTERVAL_NEVER);
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### URL Session module
|
|
76
|
+
For large downloads that continue even if the app is suspended, use `com.titaniumsdk.urlSession`:
|
|
77
|
+
|
|
78
|
+
```javascript
|
|
79
|
+
const urlSession = require('com.titaniumsdk.urlSession');
|
|
80
|
+
|
|
81
|
+
const config = urlSession.createSessionConfiguration({
|
|
82
|
+
identifier: 'com.myapp.downloads'
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
const session = urlSession.createSession({
|
|
86
|
+
configuration: config
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// Start a download task
|
|
90
|
+
session.downloadTask({
|
|
91
|
+
url: 'https://example.com/largefile.zip'
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// Monitor progress
|
|
95
|
+
session.addEventListener('downloadprogress', (e) => {
|
|
96
|
+
Ti.API.info(`Progress: ${e.totalBytesWritten}/${e.totalBytesExpectedToWrite}`);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
session.addEventListener('downloadcompleted', (e) => {
|
|
100
|
+
Ti.API.info(`Download saved to: ${e.data}`);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
session.addEventListener('sessioncompleted', (e) => {
|
|
104
|
+
if (!e.success) {
|
|
105
|
+
Ti.API.error(`Session error: ${e.errorDescription}`);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// Invalidate session when no longer needed
|
|
110
|
+
session.invalidateAndCancel();
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## 3. iCloud services and backup control
|
|
114
|
+
|
|
115
|
+
### Disable individual backup (best practice)
|
|
116
|
+
Apple rejects apps that upload unnecessary data to iCloud. Disable backup for temporary or recreatable files.
|
|
117
|
+
|
|
118
|
+
```javascript
|
|
119
|
+
const file = Ti.Filesystem.getFile(Ti.Filesystem.applicationDataDirectory, 'cache.dat');
|
|
120
|
+
file.remoteBackup = false; // Prevents upload to iCloud/iTunes
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Recursive folder backup disable
|
|
124
|
+
```javascript
|
|
125
|
+
function disableiCloudBackup(folder) {
|
|
126
|
+
const dir = Ti.Filesystem.getFile(folder);
|
|
127
|
+
const files = dir.getDirectoryListing();
|
|
128
|
+
files.forEach((name) => {
|
|
129
|
+
const f = Ti.Filesystem.getFile(folder, name);
|
|
130
|
+
f.remoteBackup = false;
|
|
131
|
+
if (f.isDirectory()) disableiCloudBackup(f.nativePath);
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## 4. WatchKit and Ti.WatchSession
|
|
137
|
+
|
|
138
|
+
For watchOS 2+, use `Ti.WatchSession` for bidirectional communication.
|
|
139
|
+
|
|
140
|
+
### Activate session
|
|
141
|
+
```javascript
|
|
142
|
+
if (Ti.WatchSession.isSupported) {
|
|
143
|
+
Ti.WatchSession.activateSession();
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Send message (immediate)
|
|
148
|
+
```javascript
|
|
149
|
+
if (Ti.WatchSession.isReachable) {
|
|
150
|
+
Ti.WatchSession.sendMessage({
|
|
151
|
+
orderId: '123',
|
|
152
|
+
status: 'shipped'
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Receive data from Watch
|
|
158
|
+
```javascript
|
|
159
|
+
Ti.WatchSession.addEventListener('receivemessage', (e) => {
|
|
160
|
+
Ti.API.info(`Message from Watch: ${e.message}`);
|
|
161
|
+
});
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Provisioning profiles for Watch
|
|
165
|
+
Both the iOS app and WatchKit extension need separate provisioning profiles. Configure in `tiapp.xml`:
|
|
166
|
+
```xml
|
|
167
|
+
<ios>
|
|
168
|
+
<extensions>
|
|
169
|
+
<extension projectPath="extensions/WatchApp/WatchApp.xcodeproj">
|
|
170
|
+
<target name="WatchApp Extension">
|
|
171
|
+
<provisioning-profiles>
|
|
172
|
+
<device>WATCH_DEV_PROFILE_UUID</device>
|
|
173
|
+
<dist-appstore>WATCH_DIST_PROFILE_UUID</dist-appstore>
|
|
174
|
+
</provisioning-profiles>
|
|
175
|
+
</target>
|
|
176
|
+
</extension>
|
|
177
|
+
</extensions>
|
|
178
|
+
</ios>
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Note: for Xcode 8+, ensure your Team ID matches across all targets.
|
|
182
|
+
|
|
183
|
+
## 5. SiriKit and Siri intents
|
|
184
|
+
|
|
185
|
+
Allows your app to respond to Siri voice commands (Messaging, Payments, Workouts).
|
|
186
|
+
|
|
187
|
+
### Configuration in tiapp.xml
|
|
188
|
+
```xml
|
|
189
|
+
<key>NSSiriUsageDescription</key>
|
|
190
|
+
<string>Siri will use your voice to send messages in this app.</string>
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### Siri extensions
|
|
194
|
+
Create an Intents Extension in Xcode and add it to the `extensions/` folder. Then register it in `tiapp.xml`:
|
|
195
|
+
```xml
|
|
196
|
+
<ios>
|
|
197
|
+
<extensions>
|
|
198
|
+
<extension projectPath="extensions/SiriIntent/SiriIntent.xcodeproj">
|
|
199
|
+
<target name="SiriIntent">
|
|
200
|
+
<provisioning-profiles>
|
|
201
|
+
<device>PROVISIONING_PROFILE_UUID</device>
|
|
202
|
+
</provisioning-profiles>
|
|
203
|
+
</target>
|
|
204
|
+
</extension>
|
|
205
|
+
</extensions>
|
|
206
|
+
</ios>
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### Implementation steps
|
|
210
|
+
1. Register App ID with SiriKit capability in Apple Developer Portal.
|
|
211
|
+
2. Create Intents Extension in Xcode (File > New > Target > Intents Extension).
|
|
212
|
+
3. Configure entitlements (add `com.apple.developer.siri`).
|
|
213
|
+
4. Add extension to project (place in `extensions/` folder).
|
|
214
|
+
5. Register in `tiapp.xml` with provisioning profiles for the extension target.
|
|
215
|
+
6. Handle intents in the native extension code (Swift/Objective-C).
|
|
216
|
+
|
|
217
|
+
Note: SiriKit requires native code in the Intent Extension. The Titanium app receives results via `continueactivity` events.
|
|
218
|
+
|
|
219
|
+
## 6. Spotlight search (Core Spotlight)
|
|
220
|
+
|
|
221
|
+
Indexes your app content to appear in global iOS search results.
|
|
222
|
+
|
|
223
|
+
```javascript
|
|
224
|
+
const itemAttr = Ti.App.iOS.createSearchableItemAttributeSet({
|
|
225
|
+
itemContentType: Ti.App.iOS.UTTYPE_TEXT,
|
|
226
|
+
title: 'My Article',
|
|
227
|
+
contentDescription: 'Content description...',
|
|
228
|
+
keywords: ['titanium', 'help']
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
const item = Ti.App.iOS.createSearchableItem({
|
|
232
|
+
uniqueIdentifier: 'id-123',
|
|
233
|
+
domainIdentifier: 'articles',
|
|
234
|
+
attributeSet: itemAttr
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
const indexer = Ti.App.iOS.createSearchableIndex();
|
|
238
|
+
indexer.addToDefaultSearchableIndex([item], (e) => {
|
|
239
|
+
if (e.success) Ti.API.info('Indexed!');
|
|
240
|
+
});
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
## 7. Core Motion module
|
|
244
|
+
|
|
245
|
+
### Overview
|
|
246
|
+
Core Motion provides access to hardware sensors: accelerometer, gyroscope, magnetometer, and more.
|
|
247
|
+
|
|
248
|
+
Requirements:
|
|
249
|
+
- Add module to `tiapp.xml`:
|
|
250
|
+
```xml
|
|
251
|
+
<modules>
|
|
252
|
+
<module platform="iphone">ti.coremotion</module>
|
|
253
|
+
</modules>
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
- Can only test on device, not simulator
|
|
257
|
+
- Motion Activity permission required for Activity API
|
|
258
|
+
|
|
259
|
+
### Basic workflow
|
|
260
|
+
|
|
261
|
+
1. Require the module:
|
|
262
|
+
```javascript
|
|
263
|
+
var CoreMotion = require('ti.coremotion');
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
2. Check availability:
|
|
267
|
+
```javascript
|
|
268
|
+
var Accelerometer = CoreMotion.createAccelerometer();
|
|
269
|
+
if (Accelerometer.isAccelerometerAvailable()) {
|
|
270
|
+
// Use accelerometer
|
|
271
|
+
}
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
3. Start updates:
|
|
275
|
+
```javascript
|
|
276
|
+
Accelerometer.setAccelerometerUpdateInterval(1000); // 1 second
|
|
277
|
+
Accelerometer.startAccelerometerUpdates((e) => {
|
|
278
|
+
if (e.success) {
|
|
279
|
+
const data = e.acceleration;
|
|
280
|
+
Ti.API.info(`X: ${data.x} Y: ${data.y} Z: ${data.z}`);
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
4. Stop when done:
|
|
286
|
+
```javascript
|
|
287
|
+
Accelerometer.stopAccelerometerUpdates();
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
### Coordinate system
|
|
291
|
+
Hold device in portrait mode, screen facing you:
|
|
292
|
+
- X-axis: width (positive = right, negative = left)
|
|
293
|
+
- Y-axis: height (positive = up, negative = down)
|
|
294
|
+
- Z-axis: through screen (positive = toward screen, negative = behind)
|
|
295
|
+
|
|
296
|
+
### Accelerometer
|
|
297
|
+
|
|
298
|
+
Measures g-force acceleration along three axes.
|
|
299
|
+
|
|
300
|
+
```javascript
|
|
301
|
+
var CoreMotion = require('ti.coremotion');
|
|
302
|
+
var Accelerometer = CoreMotion.createAccelerometer();
|
|
303
|
+
|
|
304
|
+
if (Accelerometer.isAccelerometerAvailable()) {
|
|
305
|
+
Accelerometer.setAccelerometerUpdateInterval(100);
|
|
306
|
+
Accelerometer.startAccelerometerUpdates(function(e) {
|
|
307
|
+
if (e.success) {
|
|
308
|
+
var data = e.acceleration;
|
|
309
|
+
updateDisplay(data.x, data.y, data.z);
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
Use for: shake detection, device orientation, movement detection.
|
|
316
|
+
|
|
317
|
+
### Gyroscope
|
|
318
|
+
|
|
319
|
+
Measures rotational rate along three axes (radians).
|
|
320
|
+
|
|
321
|
+
```javascript
|
|
322
|
+
var Gyroscope = CoreMotion.createGyroscope();
|
|
323
|
+
|
|
324
|
+
if (Gyroscope.isGyroAvailable()) {
|
|
325
|
+
Gyroscope.setGyroUpdateInterval(100);
|
|
326
|
+
Gyroscope.startGyroUpdates(function(e) {
|
|
327
|
+
if (e.success) {
|
|
328
|
+
var data = e.rotationRate;
|
|
329
|
+
Ti.API.info('Rotation: X=' + data.x + ' Y=' + data.y + ' Z=' + data.z);
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
Use for: rotation gestures, 3D motion tracking, enhanced UI.
|
|
336
|
+
|
|
337
|
+
### Magnetometer
|
|
338
|
+
|
|
339
|
+
Measures magnetic field strength (microteslas). Acts as a digital compass.
|
|
340
|
+
|
|
341
|
+
```javascript
|
|
342
|
+
var Magnetometer = CoreMotion.createMagnetometer();
|
|
343
|
+
|
|
344
|
+
if (Magnetometer.isMagnetometerAvailable()) {
|
|
345
|
+
Magnetometer.startMagnetometerUpdates();
|
|
346
|
+
// Or poll manually:
|
|
347
|
+
var data = Magnetometer.getMagnetometerData();
|
|
348
|
+
Ti.API.info('Magnetic field: ' + JSON.stringify(data.magneticField));
|
|
349
|
+
}
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
Use for: compass direction, magnetic field detection.
|
|
353
|
+
|
|
354
|
+
### Device motion
|
|
355
|
+
|
|
356
|
+
Combines accelerometer, gyroscope, and magnetometer for attitude and user acceleration.
|
|
357
|
+
|
|
358
|
+
```javascript
|
|
359
|
+
var DeviceMotion = CoreMotion.createDeviceMotion();
|
|
360
|
+
|
|
361
|
+
if (DeviceMotion.isDeviceMotionAvailable()) {
|
|
362
|
+
DeviceMotion.setDeviceMotionUpdateInterval(500);
|
|
363
|
+
|
|
364
|
+
// Check available reference frames
|
|
365
|
+
var frames = DeviceMotion.availableAttitudeReferenceFrames();
|
|
366
|
+
|
|
367
|
+
if (frames & CoreMotion.ATTITUDE_REFERENCE_FRAME_X_TRUE_NORTH_Z_VERTICAL) {
|
|
368
|
+
// Use true north reference
|
|
369
|
+
DeviceMotion.startDeviceMotionUpdatesUsingReferenceFrame(
|
|
370
|
+
{ referenceFrame: CoreMotion.ATTITUDE_REFERENCE_FRAME_X_TRUE_NORTH_Z_VERTICAL },
|
|
371
|
+
(e) => {
|
|
372
|
+
if (e.success) {
|
|
373
|
+
// Attitude: orientation (pitch, roll, yaw)
|
|
374
|
+
const attitude = e.attitude;
|
|
375
|
+
Ti.API.info(`Pitch: ${attitude.pitch} Roll: ${attitude.roll} Yaw: ${attitude.yaw}`);
|
|
376
|
+
|
|
377
|
+
// User acceleration: force applied by user (not gravity)
|
|
378
|
+
const userAccel = e.userAcceleration;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
Attitude formats:
|
|
387
|
+
- Pitch/Roll/Yaw (Euler angles)
|
|
388
|
+
- Quaternion (w, x, y, z)
|
|
389
|
+
- Rotation Matrix (m11-m33)
|
|
390
|
+
|
|
391
|
+
Reference frames:
|
|
392
|
+
- `ATTITUDE_REFERENCE_FRAME_X_ARBITRARY_Z_VERTICAL` - default
|
|
393
|
+
- `ATTITUDE_REFERENCE_FRAME_X_ARBITRARY_CORRECTED_Z_VERTICAL` - uses magnetometer for yaw
|
|
394
|
+
- `ATTITUDE_REFERENCE_FRAME_X_MAGNETIC_NORTH_Z_VERTICAL` - magnetic north
|
|
395
|
+
- `ATTITUDE_REFERENCE_FRAME_X_TRUE_NORTH_Z_VERTICAL` - true north (requires location)
|
|
396
|
+
|
|
397
|
+
### Activity API
|
|
398
|
+
|
|
399
|
+
```javascript
|
|
400
|
+
const MotionActivity = CoreMotion.createMotionActivity();
|
|
401
|
+
|
|
402
|
+
MotionActivity.startActivityUpdates((e) => {
|
|
403
|
+
const activity = e.activity;
|
|
404
|
+
|
|
405
|
+
// Check confidence
|
|
406
|
+
if (activity.confidence !== CoreMotion.MOTION_ACTIVITY_CONFIDENCE_LOW) {
|
|
407
|
+
if (activity.walking) {
|
|
408
|
+
Ti.API.info('User is walking');
|
|
409
|
+
} else if (activity.running) {
|
|
410
|
+
Ti.API.info('User is running');
|
|
411
|
+
} else if (activity.automotive) {
|
|
412
|
+
Ti.API.info('User in vehicle');
|
|
413
|
+
} else if (activity.stationary) {
|
|
414
|
+
Ti.API.info('Device stationary');
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
});
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
Query historical activity:
|
|
421
|
+
```javascript
|
|
422
|
+
var endDate = new Date();
|
|
423
|
+
var startDate = new Date(endDate.getTime() - 60 * 60 * 1000); // 1 hour ago
|
|
424
|
+
|
|
425
|
+
MotionActivity.queryActivity({
|
|
426
|
+
start: startDate,
|
|
427
|
+
end: endDate
|
|
428
|
+
}, function(e) {
|
|
429
|
+
var activities = e.activities;
|
|
430
|
+
// Process historical data
|
|
431
|
+
});
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
Requires Motion Activity permission in `tiapp.xml`:
|
|
435
|
+
```xml
|
|
436
|
+
<key>NSMotionUsageDescription</key>
|
|
437
|
+
<string>Need motion data for fitness tracking</string>
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
### Pedometer
|
|
441
|
+
|
|
442
|
+
```javascript
|
|
443
|
+
const Pedometer = CoreMotion.createPedometer();
|
|
444
|
+
|
|
445
|
+
if (Pedometer.isStepCountingAvailable()) {
|
|
446
|
+
// Start live updates
|
|
447
|
+
Pedometer.startPedometerUpdates({
|
|
448
|
+
start: new Date(new Date().getTime() - (60 * 60 * 1000)) // From 1 hour ago
|
|
449
|
+
}, (e) => {
|
|
450
|
+
Ti.API.info(`Steps: ${e.numberOfSteps}`);
|
|
451
|
+
Ti.API.info(`Distance: ${e.distance} meters`);
|
|
452
|
+
Ti.API.info(`Floors up: ${e.floorsAscended}`);
|
|
453
|
+
Ti.API.info(`Floors down: ${e.floorsDescended}`);
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// Query historical data
|
|
458
|
+
var endDate = new Date();
|
|
459
|
+
var startDate = new Date(endDate.getTime() - 24 * 60 * 60 * 1000); // 24 hours ago
|
|
460
|
+
|
|
461
|
+
Pedometer.queryPedometerData({
|
|
462
|
+
start: startDate,
|
|
463
|
+
end: endDate
|
|
464
|
+
}, function(e) {
|
|
465
|
+
Ti.API.info('Steps in last 24h: ' + e.numberOfSteps);
|
|
466
|
+
});
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
Use for: fitness apps, step challenges, activity tracking.
|
|
470
|
+
|
|
471
|
+
### Core Motion best practices
|
|
472
|
+
|
|
473
|
+
1. Always check availability; not all devices have all sensors.
|
|
474
|
+
2. Set appropriate update intervals; high frequency means more CPU/battery.
|
|
475
|
+
3. Stop updates when not needed to conserve battery.
|
|
476
|
+
4. Handle errors gracefully; sensors can fail or be unavailable.
|
|
477
|
+
5. Test on physical devices; sensors do not work in simulator.
|
|
478
|
+
|
|
479
|
+
## 8. Handoff user activities
|
|
480
|
+
|
|
481
|
+
```javascript
|
|
482
|
+
const UserActivity = Ti.App.iOS.createUserActivity({
|
|
483
|
+
activityType: 'com.myapp.reading-article',
|
|
484
|
+
title: 'Reading: Titanium SDK Guide',
|
|
485
|
+
userInfo: {
|
|
486
|
+
articleId: '123',
|
|
487
|
+
scrollPosition: 450
|
|
488
|
+
},
|
|
489
|
+
webpageURL: 'https://myapp.com/articles/123' // For web fallback
|
|
490
|
+
});
|
|
491
|
+
|
|
492
|
+
// Mark as current activity
|
|
493
|
+
UserActivity.becomeCurrent();
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
### Handling incoming Handoff
|
|
497
|
+
|
|
498
|
+
```javascript
|
|
499
|
+
Ti.App.iOS.addEventListener('continueactivity', function(e) {
|
|
500
|
+
if (e.activityType === 'com.myapp.reading-article') {
|
|
501
|
+
var userInfo = e.userInfo;
|
|
502
|
+
|
|
503
|
+
// Restore activity state
|
|
504
|
+
openArticle(userInfo.articleId, {
|
|
505
|
+
scrollTo: userInfo.scrollPosition
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
});
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
### Invalidating activities
|
|
512
|
+
|
|
513
|
+
```javascript
|
|
514
|
+
// When user closes article
|
|
515
|
+
UserActivity.invalidate();
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
### Declaring activity types in tiapp.xml
|
|
519
|
+
|
|
520
|
+
```xml
|
|
521
|
+
<key>NSUserActivityTypes</key>
|
|
522
|
+
<array>
|
|
523
|
+
<string>com.myapp.reading-article</string>
|
|
524
|
+
<string>com.myapp.editing-document</string>
|
|
525
|
+
<string>com.myapp.viewing-product</string>
|
|
526
|
+
</array>
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
### Requirements
|
|
530
|
+
- Both devices must be signed into the same iCloud account
|
|
531
|
+
- Bluetooth LE must be enabled on both devices
|
|
532
|
+
- Both devices must be on the same Wi-Fi network
|
|
533
|
+
- Test with Safari first to verify Handoff works between your devices
|
|
534
|
+
|
|
535
|
+
## 9. Additional iOS features
|
|
536
|
+
|
|
537
|
+
### 3D Touch (Force Touch)
|
|
538
|
+
|
|
539
|
+
```javascript
|
|
540
|
+
// Check for 3D Touch support
|
|
541
|
+
if (Ti.Platform.forceTouchSupported) {
|
|
542
|
+
view.addEventListener('touchstart', (e) => {
|
|
543
|
+
const force = e.force || 0;
|
|
544
|
+
const maxForce = e.maximumForce || 1;
|
|
545
|
+
|
|
546
|
+
if (force / maxForce > 0.75) {
|
|
547
|
+
// Deep press
|
|
548
|
+
showQuickActions();
|
|
549
|
+
}
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
### Haptic feedback
|
|
555
|
+
|
|
556
|
+
```javascript
|
|
557
|
+
// Generate haptic feedback
|
|
558
|
+
const generator = Ti.UI.iOS.createHapticFeedbackGenerator();
|
|
559
|
+
|
|
560
|
+
// Impact feedback (light, medium, heavy)
|
|
561
|
+
generator.impactOccurred(Ti.UI.iOS.HAPTIC_FEEDBACK_STYLE_MEDIUM);
|
|
562
|
+
|
|
563
|
+
// Selection feedback
|
|
564
|
+
generator.selectionChanged();
|
|
565
|
+
|
|
566
|
+
// Notification feedback (success, warning, error)
|
|
567
|
+
generator.notificationOccurred(Ti.UI.iOS.HAPTIC_FEEDBACK_TYPE_SUCCESS);
|
|
568
|
+
```
|
|
569
|
+
|
|
570
|
+
### Document interaction
|
|
571
|
+
|
|
572
|
+
Open documents in other apps:
|
|
573
|
+
|
|
574
|
+
```javascript
|
|
575
|
+
var docController = Ti.UI.iOS.createDocumentViewer({
|
|
576
|
+
url: file.nativePath
|
|
577
|
+
});
|
|
578
|
+
|
|
579
|
+
docController.addEventListener('complete', function(e) {
|
|
580
|
+
Ti.API.info('Document interaction complete');
|
|
581
|
+
});
|
|
582
|
+
|
|
583
|
+
win.add(docController);
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
## Best practices summary
|
|
587
|
+
|
|
588
|
+
1. Background services: use sparingly; stop when done.
|
|
589
|
+
2. Core Motion: check availability and test on device.
|
|
590
|
+
3. Spotlight: index content as it changes.
|
|
591
|
+
4. Handoff: provide a web URL fallback.
|
|
592
|
+
5. iCloud: use for small synced data, not large files.
|
|
593
|
+
6. SiriKit: requires native extension; use only if beneficial.
|
|
594
|
+
7. Permissions: always include usage descriptions in `tiapp.xml`.
|
|
595
|
+
8. Testing: many features require physical device testing.
|