@maccesar/titools 2.0.7 → 2.2.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/{AGENTS-TEMPLATE.md → AGENTS-VERCEL-RESEARCH.md} +16 -18
- package/README.md +250 -168
- package/agents/{ti-researcher.md → ti-pro.md} +10 -10
- package/bin/titools.js +16 -12
- package/lib/cleanup.js +95 -0
- package/lib/commands/agents.js +146 -70
- package/lib/commands/skills.js +349 -0
- package/lib/commands/uninstall.js +189 -154
- package/lib/commands/update.js +104 -21
- package/lib/config.js +26 -20
- package/lib/downloader.js +1 -1
- package/lib/installer.js +37 -97
- package/lib/platform.js +9 -5
- package/lib/prompts/checkboxCancel.js +264 -0
- package/lib/prompts/selectCancel.js +204 -0
- package/lib/symlink.js +19 -7
- package/lib/utils.js +17 -17
- package/package.json +10 -10
- package/skills/alloy-guides/SKILL.md +8 -30
- package/skills/alloy-guides/references/CLI_TASKS.md +14 -24
- package/skills/alloy-guides/references/CONCEPTS.md +5 -25
- package/skills/alloy-guides/references/CONTROLLERS.md +2 -19
- package/skills/alloy-guides/references/MODELS.md +40 -10
- package/skills/alloy-guides/references/PURGETSS.md +1 -11
- package/skills/alloy-guides/references/VIEWS_DYNAMIC.md +8 -14
- package/skills/alloy-guides/references/VIEWS_STYLES.md +14 -25
- package/skills/alloy-guides/references/VIEWS_WITHOUT_CONTROLLERS.md +2 -8
- package/skills/alloy-guides/references/VIEWS_XML.md +29 -40
- package/skills/alloy-guides/references/WIDGETS.md +1 -17
- package/skills/alloy-howtos/SKILL.md +1 -22
- package/skills/alloy-howtos/references/best_practices.md +0 -17
- package/skills/alloy-howtos/references/cli_reference.md +1 -23
- package/skills/alloy-howtos/references/config_files.md +86 -15
- package/skills/alloy-howtos/references/custom_tags.md +14 -13
- package/skills/alloy-howtos/references/debugging_troubleshooting.md +3 -22
- package/skills/alloy-howtos/references/samples.md +4 -14
- package/skills/purgetss/SKILL.md +11 -37
- package/skills/purgetss/references/EXAMPLES.md +106 -17
- package/skills/purgetss/references/animation-system.md +0 -67
- package/skills/purgetss/references/apply-directive.md +0 -18
- package/skills/purgetss/references/arbitrary-values.md +0 -25
- package/skills/purgetss/references/class-index.md +0 -46
- package/skills/purgetss/references/cli-commands.md +0 -41
- package/skills/purgetss/references/configurable-properties.md +0 -55
- package/skills/purgetss/references/custom-rules.md +0 -14
- package/skills/purgetss/references/customization-deep-dive.md +0 -49
- package/skills/purgetss/references/dynamic-component-creation.md +56 -37
- package/skills/purgetss/references/grid-layout.md +0 -32
- package/skills/purgetss/references/icon-fonts.md +0 -50
- package/skills/purgetss/references/installation-setup.md +61 -32
- package/skills/purgetss/references/migration-guide.md +127 -0
- package/skills/purgetss/references/opacity-modifier.md +0 -34
- package/skills/purgetss/references/performance-tips.md +261 -0
- package/skills/purgetss/references/platform-modifiers.md +0 -46
- package/skills/purgetss/references/smart-mappings.md +0 -12
- package/skills/purgetss/references/tikit-components.md +393 -0
- package/skills/purgetss/references/titanium-resets.md +1 -27
- package/skills/purgetss/references/ui-ux-design.md +467 -117
- package/skills/{alloy-expert → ti-expert}/SKILL.md +45 -54
- package/skills/ti-expert/references/alloy-builtins.md +387 -0
- package/skills/{alloy-expert → ti-expert}/references/alloy-structure.md +147 -40
- package/skills/ti-expert/references/anti-patterns.md +90 -0
- package/skills/ti-expert/references/cli-expert.md +109 -0
- package/skills/{alloy-expert → ti-expert}/references/code-conventions.md +182 -49
- package/skills/{alloy-expert → ti-expert}/references/controller-patterns.md +103 -93
- package/skills/{alloy-expert → ti-expert}/references/error-handling.md +12 -12
- package/skills/{alloy-expert → ti-expert}/references/examples.md +144 -85
- package/skills/{alloy-expert → ti-expert}/references/migration-patterns.md +38 -36
- package/skills/{alloy-expert → ti-expert}/references/patterns.md +8 -8
- package/skills/ti-expert/references/performance-listview.md +251 -0
- package/skills/{alloy-expert/references/performance-patterns.md → ti-expert/references/performance-optimization.md} +67 -283
- package/skills/{alloy-expert/references/security-patterns.md → ti-expert/references/security-device.md} +12 -295
- package/skills/ti-expert/references/security-fundamentals.md +284 -0
- package/skills/{alloy-expert → ti-expert}/references/state-management.md +10 -10
- package/skills/ti-expert/references/testing-e2e-ci.md +432 -0
- package/skills/ti-expert/references/testing-unit.md +433 -0
- package/skills/ti-expert/references/theming.md +394 -0
- package/skills/ti-guides/SKILL.md +1 -14
- package/skills/ti-guides/references/advanced-data-and-images.md +126 -10
- package/skills/ti-guides/references/app-distribution.md +48 -10
- package/skills/ti-guides/references/application-frameworks.md +9 -2
- package/skills/ti-guides/references/cli-reference.md +338 -82
- package/skills/ti-guides/references/coding-best-practices.md +14 -4
- package/skills/ti-guides/references/commonjs-advanced.md +103 -6
- package/skills/ti-guides/references/hello-world.md +5 -2
- package/skills/ti-guides/references/hyperloop-native-access.md +439 -46
- package/skills/ti-guides/references/javascript-primer.md +4 -3
- package/skills/ti-guides/references/resources.md +0 -1
- package/skills/ti-guides/references/style-and-conventions.md +57 -9
- package/skills/ti-guides/references/tiapp-config.md +48 -6
- package/skills/ti-howtos/SKILL.md +2 -27
- package/skills/ti-howtos/references/android-platform-deep-dives.md +41 -90
- package/skills/ti-howtos/references/automation-fastlane-appium.md +15 -14
- package/skills/ti-howtos/references/buffer-codec-streams.md +22 -22
- package/skills/ti-howtos/references/cross-platform-development.md +68 -37
- package/skills/ti-howtos/references/debugging-profiling.md +8 -53
- package/skills/ti-howtos/references/extending-titanium.md +12 -51
- package/skills/ti-howtos/references/google-maps-v2.md +30 -29
- package/skills/ti-howtos/references/ios-map-kit.md +19 -25
- package/skills/ti-howtos/references/ios-platform-deep-dives.md +95 -283
- package/skills/ti-howtos/references/local-data-sources.md +45 -37
- package/skills/ti-howtos/references/location-and-maps.md +47 -28
- package/skills/ti-howtos/references/media-apis.md +84 -27
- package/skills/ti-howtos/references/notification-services.md +40 -89
- package/skills/ti-howtos/references/remote-data-sources.md +21 -36
- package/skills/ti-howtos/references/tutorials.md +79 -39
- package/skills/ti-howtos/references/using-modules.md +4 -30
- package/skills/ti-howtos/references/web-content-integration.md +28 -45
- package/skills/ti-howtos/references/webpack-build-pipeline.md +54 -18
- package/skills/ti-ui/SKILL.md +1 -21
- package/skills/ti-ui/references/accessibility-deep-dive.md +71 -25
- package/skills/ti-ui/references/animation-and-matrices.md +0 -47
- package/skills/ti-ui/references/application-structures.md +0 -46
- package/skills/ti-ui/references/custom-fonts-styling.md +9 -57
- package/skills/ti-ui/references/event-handling.md +32 -40
- package/skills/ti-ui/references/gestures.md +0 -40
- package/skills/ti-ui/references/icons-and-splash-screens.md +21 -44
- package/skills/ti-ui/references/layouts-and-positioning.md +47 -39
- package/skills/ti-ui/references/listviews-and-performance.md +187 -73
- package/skills/ti-ui/references/orientation.md +57 -71
- package/skills/ti-ui/references/platform-ui-android.md +114 -48
- package/skills/ti-ui/references/platform-ui-ios.md +63 -38
- package/skills/ti-ui/references/scrolling-views.md +14 -23
- package/skills/ti-ui/references/tableviews.md +6 -56
- package/lib/commands/install.js +0 -188
- package/skills/alloy-expert/references/anti-patterns.md +0 -133
- package/skills/alloy-expert/references/testing.md +0 -872
- package/skills/ti-guides/references/alloy-cli-advanced.md +0 -84
- package/skills/ti-guides/references/alloy-data-mastery.md +0 -29
- package/skills/ti-guides/references/alloy-widgets-and-themes.md +0 -19
- /package/skills/{alloy-expert → ti-expert}/assets/ControllerAutoCleanup.js +0 -0
- /package/skills/{alloy-expert → ti-expert}/references/contracts.md +0 -0
|
@@ -1,68 +1,5 @@
|
|
|
1
1
|
# iOS Platform Deep Dives
|
|
2
2
|
|
|
3
|
-
## Table of Contents
|
|
4
|
-
|
|
5
|
-
- [iOS Platform Deep Dives](#ios-platform-deep-dives)
|
|
6
|
-
- [Table of Contents](#table-of-contents)
|
|
7
|
-
- [1. iOS 17+ Privacy Requirements (Critical)](#1-ios-17-privacy-requirements-critical)
|
|
8
|
-
- [PrivacyInfo.xcprivacy File](#privacyinfoxcprivacy-file)
|
|
9
|
-
- [2. Background Services \& Silent Push](#2-background-services--silent-push)
|
|
10
|
-
- [Overview](#overview)
|
|
11
|
-
- [Silent Push (Background Update)](#silent-push-background-update)
|
|
12
|
-
- [3. iCloud Services \& Backup Control](#3-icloud-services--backup-control)
|
|
13
|
-
- [Disable Individual Backup (Best Practice)](#disable-individual-backup-best-practice)
|
|
14
|
-
- [Recursive Folder Backup Disable](#recursive-folder-backup-disable)
|
|
15
|
-
- [4. WatchKit \& Ti.WatchSession](#4-watchkit--tiwatchsession)
|
|
16
|
-
- [Activate Session](#activate-session)
|
|
17
|
-
- [Send Message (Immediate)](#send-message-immediate)
|
|
18
|
-
- [Receive Data from Watch](#receive-data-from-watch)
|
|
19
|
-
- [5. SiriKit \& Siri Intents](#5-sirikit--siri-intents)
|
|
20
|
-
- [Configuration in tiapp.xml](#configuration-in-tiappxml)
|
|
21
|
-
- [Siri Extensions](#siri-extensions)
|
|
22
|
-
- [6. Spotlight Search (Core Spotlight)](#6-spotlight-search-core-spotlight)
|
|
23
|
-
- [7. Core Motion Module](#7-core-motion-module)
|
|
24
|
-
- [Overview](#overview-1)
|
|
25
|
-
- [Basic Workflow](#basic-workflow)
|
|
26
|
-
- [Coordinate System](#coordinate-system)
|
|
27
|
-
- [Accelerometer](#accelerometer)
|
|
28
|
-
- [Gyroscope](#gyroscope)
|
|
29
|
-
- [Magnetometer](#magnetometer)
|
|
30
|
-
- [Device Motion](#device-motion)
|
|
31
|
-
- [Activity API](#activity-api)
|
|
32
|
-
- [Pedometer](#pedometer)
|
|
33
|
-
- [Core Motion Best Practices](#core-motion-best-practices)
|
|
34
|
-
- [3. Spotlight Search](#3-spotlight-search)
|
|
35
|
-
- [Overview](#overview-2)
|
|
36
|
-
- [Creating Searchable Items](#creating-searchable-items)
|
|
37
|
-
- [Indexing Items](#indexing-items)
|
|
38
|
-
- [Deleting from Index](#deleting-from-index)
|
|
39
|
-
- [Handling Search Results](#handling-search-results)
|
|
40
|
-
- [Best Practices](#best-practices)
|
|
41
|
-
- [8. Handoff User Activities](#8-handoff-user-activities)
|
|
42
|
-
- [Handling Incoming Handoff](#handling-incoming-handoff)
|
|
43
|
-
- [Invalidating Activities](#invalidating-activities)
|
|
44
|
-
- [Declaring Activity Types in tiapp.xml](#declaring-activity-types-in-tiappxml)
|
|
45
|
-
- [5. iCloud Services](#5-icloud-services)
|
|
46
|
-
- [Keychain Storage](#keychain-storage)
|
|
47
|
-
- [Document Picker](#document-picker)
|
|
48
|
-
- [CloudKit](#cloudkit)
|
|
49
|
-
- [6. WatchKit Integration](#6-watchkit-integration)
|
|
50
|
-
- [Overview](#overview-3)
|
|
51
|
-
- [Integration Steps](#integration-steps)
|
|
52
|
-
- [Watch Connectivity](#watch-connectivity)
|
|
53
|
-
- [7. SiriKit Integration](#7-sirikit-integration)
|
|
54
|
-
- [Overview](#overview-4)
|
|
55
|
-
- [Supported Intents (iOS 10+)](#supported-intents-ios-10)
|
|
56
|
-
- [Implementation](#implementation)
|
|
57
|
-
- [Voice Shortcuts (iOS 12+)](#voice-shortcuts-ios-12)
|
|
58
|
-
- [8. Additional iOS Features](#8-additional-ios-features)
|
|
59
|
-
- [3D Touch (Force Touch)](#3d-touch-force-touch)
|
|
60
|
-
- [Haptic Feedback](#haptic-feedback)
|
|
61
|
-
- [Document Interaction](#document-interaction)
|
|
62
|
-
- [Best Practices Summary](#best-practices-summary)
|
|
63
|
-
|
|
64
|
-
---
|
|
65
|
-
|
|
66
3
|
## 1. iOS 17+ Privacy Requirements (Critical)
|
|
67
4
|
Apple requires declaring the use of certain APIs to prevent "fingerprinting".
|
|
68
5
|
|
|
@@ -108,12 +45,71 @@ Allows waking up the app to download content without showing a notification to t
|
|
|
108
45
|
Ti.App.iOS.addEventListener('silentpush', (e) => {
|
|
109
46
|
// Start download or update
|
|
110
47
|
Ti.API.info(`Data received: ${JSON.stringify(e)}`);
|
|
111
|
-
|
|
48
|
+
|
|
112
49
|
// Mandatory to call upon completion (max 30 seconds)
|
|
113
50
|
Ti.App.iOS.endBackgroundHandler(e.handlerId);
|
|
114
51
|
});
|
|
115
52
|
```
|
|
116
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 the `com.titaniumsdk.urlSession` module:
|
|
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
|
+
|
|
117
113
|
## 3. iCloud Services & Backup Control
|
|
118
114
|
|
|
119
115
|
### Disable Individual Backup (Best Practice)
|
|
@@ -165,6 +161,25 @@ Ti.WatchSession.addEventListener('receivemessage', (e) => {
|
|
|
165
161
|
});
|
|
166
162
|
```
|
|
167
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
|
+
|
|
168
183
|
## 5. SiriKit & Siri Intents
|
|
169
184
|
|
|
170
185
|
Allows your app to respond to Siri voice commands (Messaging, Payments, Workouts).
|
|
@@ -191,6 +206,16 @@ Requires creating an **Intents Extension** in Xcode and adding it to the `extens
|
|
|
191
206
|
</ios>
|
|
192
207
|
```
|
|
193
208
|
|
|
209
|
+
### Implementation Steps
|
|
210
|
+
1. **Register App ID** with SiriKit capability in Apple Developer Portal
|
|
211
|
+
2. **Create Intent Extension** in Xcode (File > New > Target > Intents Extension)
|
|
212
|
+
3. **Configure entitlements** — add `com.apple.developer.siri` entitlement
|
|
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
|
+
|
|
194
219
|
## 6. Spotlight Search (Core Spotlight)
|
|
195
220
|
|
|
196
221
|
Indexes your app's content to appear in global iOS search results.
|
|
@@ -417,7 +442,6 @@ MotionActivity.queryActivity({
|
|
|
417
442
|
```javascript
|
|
418
443
|
const Pedometer = CoreMotion.createPedometer();
|
|
419
444
|
|
|
420
|
-
|
|
421
445
|
if (Pedometer.isStepCountingAvailable()) {
|
|
422
446
|
// Start live updates
|
|
423
447
|
Pedometer.startPedometerUpdates({
|
|
@@ -452,105 +476,6 @@ Pedometer.queryPedometerData({
|
|
|
452
476
|
4. **Handle errors gracefully** - Sensor may fail or be unavailable
|
|
453
477
|
5. **Test on physical device** - Sensors don't work in simulator
|
|
454
478
|
|
|
455
|
-
## 3. Spotlight Search
|
|
456
|
-
|
|
457
|
-
### Overview
|
|
458
|
-
Spotlight Search allows iOS to index your app's content, making it searchable from the home screen.
|
|
459
|
-
|
|
460
|
-
### Creating Searchable Items
|
|
461
|
-
|
|
462
|
-
```javascript
|
|
463
|
-
var SearchableItem = Ti.App.iOS.SearchableItem;
|
|
464
|
-
|
|
465
|
-
var item1 = SearchableItem.createSearchableItem({
|
|
466
|
-
uniqueIdentifier: 'article-123',
|
|
467
|
-
domainIdentifier: 'articles',
|
|
468
|
-
title: 'Titanium SDK Guide',
|
|
469
|
-
contentDescription: 'Complete guide to Titanium SDK development',
|
|
470
|
-
thumbnail: articleThumbnailImage, // Ti.Blob
|
|
471
|
-
keywords: ['titanium', 'mobile', 'javascript', 'ios', 'android']
|
|
472
|
-
});
|
|
473
|
-
|
|
474
|
-
var item2 = SearchableItem.createSearchableItem({
|
|
475
|
-
uniqueIdentifier: 'product-456',
|
|
476
|
-
domainIdentifier: 'products',
|
|
477
|
-
title: 'Product Name',
|
|
478
|
-
contentDescription: 'Product description here',
|
|
479
|
-
keywords: ['product', 'shopping']
|
|
480
|
-
});
|
|
481
|
-
```
|
|
482
|
-
|
|
483
|
-
### Indexing Items
|
|
484
|
-
|
|
485
|
-
```javascript
|
|
486
|
-
var SearchableIndex = Ti.App.iOS.SearchableIndex;
|
|
487
|
-
|
|
488
|
-
SearchableIndex.indexSearchableItems([item1, item2], function(e) {
|
|
489
|
-
if (e.success) {
|
|
490
|
-
Ti.API.info('Indexing successful');
|
|
491
|
-
} else {
|
|
492
|
-
Ti.API.error('Indexing failed: ' + e.error);
|
|
493
|
-
}
|
|
494
|
-
});
|
|
495
|
-
```
|
|
496
|
-
|
|
497
|
-
### Deleting from Index
|
|
498
|
-
|
|
499
|
-
```javascript
|
|
500
|
-
// Delete specific items
|
|
501
|
-
SearchableIndex.deleteSearchableItemsWithIdentifiers(['article-123'], function(e) {
|
|
502
|
-
if (e.success) {
|
|
503
|
-
Ti.API.info('Deleted successfully');
|
|
504
|
-
}
|
|
505
|
-
});
|
|
506
|
-
|
|
507
|
-
// Delete all items in domain
|
|
508
|
-
SearchableIndex.deleteSearchableItemsWithDomainIdentifiers(['articles'], function(e) {
|
|
509
|
-
if (e.success) {
|
|
510
|
-
Ti.API.info('Deleted all articles');
|
|
511
|
-
}
|
|
512
|
-
});
|
|
513
|
-
|
|
514
|
-
// Delete all
|
|
515
|
-
SearchableIndex.deleteAllSearchableItems(function(e) {
|
|
516
|
-
if (e.success) {
|
|
517
|
-
Ti.API.info('Cleared index');
|
|
518
|
-
}
|
|
519
|
-
});
|
|
520
|
-
```
|
|
521
|
-
|
|
522
|
-
### Handling Search Results
|
|
523
|
-
|
|
524
|
-
When user taps a Spotlight result, your app opens with the `continueactivity` event:
|
|
525
|
-
|
|
526
|
-
```javascript
|
|
527
|
-
Ti.App.iOS.addEventListener('continueactivity', function(e) {
|
|
528
|
-
if (e.activityType === 'com.apple.core SpotlightQuery') {
|
|
529
|
-
var identifier = e.userInfo.identifier;
|
|
530
|
-
var domain = e.userInfo.domainIdentifier;
|
|
531
|
-
|
|
532
|
-
Ti.API.info('Spotlight search selected: ' + identifier);
|
|
533
|
-
|
|
534
|
-
// Navigate to appropriate content
|
|
535
|
-
if (domain === 'articles') {
|
|
536
|
-
openArticle(identifier);
|
|
537
|
-
} else if (domain === 'products') {
|
|
538
|
-
openProduct(identifier);
|
|
539
|
-
}
|
|
540
|
-
}
|
|
541
|
-
});
|
|
542
|
-
```
|
|
543
|
-
|
|
544
|
-
**Important**: Use `NSUserActivityTypes` in tiapp.xml to declare supported activity types.
|
|
545
|
-
|
|
546
|
-
### Best Practices
|
|
547
|
-
|
|
548
|
-
1. **Index relevant content** - Don't index everything
|
|
549
|
-
2. **Use meaningful keywords** - Help users find content
|
|
550
|
-
3. **Provide thumbnails** - Improve visual recognition
|
|
551
|
-
4. **Keep index updated** - Re-index when content changes
|
|
552
|
-
5. **Delete obsolete items** - Don't clutter search results
|
|
553
|
-
|
|
554
479
|
## 8. Handoff User Activities
|
|
555
480
|
|
|
556
481
|
```javascript
|
|
@@ -601,126 +526,13 @@ UserActivity.invalidate();
|
|
|
601
526
|
</array>
|
|
602
527
|
```
|
|
603
528
|
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
```javascript
|
|
611
|
-
// Store value
|
|
612
|
-
Ti.App.iOS.setKeychainItem('username', 'john@example.com');
|
|
613
|
-
Ti.App.iOS.setKeychainItem('authToken', 'abc123token');
|
|
614
|
-
|
|
615
|
-
// Retrieve value
|
|
616
|
-
var username = Ti.App.iOS.getKeychainItem('username');
|
|
617
|
-
|
|
618
|
-
// Remove value
|
|
619
|
-
Ti.App.iOS.removeKeychainItem('authToken');
|
|
620
|
-
```
|
|
621
|
-
|
|
622
|
-
**Use for**: Syncing user credentials, preferences, small data.
|
|
623
|
-
|
|
624
|
-
**Requires** iCloud capability enabled in Xcode project.
|
|
625
|
-
|
|
626
|
-
### Document Picker
|
|
627
|
-
|
|
628
|
-
```javascript
|
|
629
|
-
const DocumentPicker = Ti.UI.iOS.createDocumentPicker({
|
|
630
|
-
mode: Ti.UI.iOS.DOCUMENT_PICKER_MODE_OPEN
|
|
631
|
-
});
|
|
632
|
-
|
|
633
|
-
DocumentPicker.addEventListener('select', (e) => {
|
|
634
|
-
const url = e.url; // File URL
|
|
635
|
-
Ti.API.info(`Selected: ${url}`);
|
|
636
|
-
|
|
637
|
-
// Read file
|
|
638
|
-
const file = Ti.Filesystem.getFile(url);
|
|
639
|
-
const contents = file.read();
|
|
640
|
-
});
|
|
641
|
-
|
|
642
|
-
DocumentPicker.addEventListener('cancel', function(e) {
|
|
643
|
-
Ti.API.info('Document picker canceled');
|
|
644
|
-
});
|
|
645
|
-
|
|
646
|
-
// Show picker
|
|
647
|
-
DocumentPicker.show();
|
|
648
|
-
```
|
|
649
|
-
|
|
650
|
-
**Modes**:
|
|
651
|
-
- `DOCUMENT_PICKER_MODE_OPEN` - Open existing document
|
|
652
|
-
- `DOCUMENT_PICKER_MODE_EXPORT` - Export to document provider
|
|
653
|
-
- `DOCUMENT_PICKER_MODE_IMPORT` - Import copy of document
|
|
654
|
-
- `DOCUMENT_PICKER_MODE_MOVE` - Move document to app's sandbox
|
|
655
|
-
|
|
656
|
-
### CloudKit
|
|
657
|
-
|
|
658
|
-
For more complex iCloud data sync, consider using CloudKit modules or Hyperloop.
|
|
659
|
-
|
|
660
|
-
## 6. WatchKit Integration
|
|
661
|
-
|
|
662
|
-
### Overview
|
|
663
|
-
Integrate Apple Watch apps built in Xcode with Titanium app.
|
|
664
|
-
|
|
665
|
-
### Integration Steps
|
|
666
|
-
|
|
667
|
-
1. **Create WatchKit app** in Xcode
|
|
668
|
-
2. **Add App Group** to both iOS and Watch targets
|
|
669
|
-
3. **Use App Group** for shared data:
|
|
670
|
-
|
|
671
|
-
```javascript
|
|
672
|
-
// In Titanium app, use App Group container
|
|
673
|
-
var container = 'group.com.myapp.shared';
|
|
674
|
-
var sharedDefaults = Ti.App.iOS.createUserDefaults({ suiteName: container });
|
|
675
|
-
|
|
676
|
-
sharedDefaults.setString('watchData', JSON.stringify(data));
|
|
677
|
-
```
|
|
678
|
-
|
|
679
|
-
4. **Watch app reads shared data** using same App Group container
|
|
680
|
-
|
|
681
|
-
### Watch Connectivity
|
|
682
|
-
|
|
683
|
-
For bidirectional communication, use Watch Connectivity framework via Hyperloop or modules.
|
|
684
|
-
|
|
685
|
-
## 7. SiriKit Integration
|
|
686
|
-
|
|
687
|
-
### Overview
|
|
688
|
-
SiriKit allows your app to handle specific intents via Siri voice commands.
|
|
689
|
-
|
|
690
|
-
### Supported Intents (iOS 10+)
|
|
691
|
-
- Messaging (send/read messages)
|
|
692
|
-
- Payments (send/request money)
|
|
693
|
-
- Workouts (start/stop/pause/resume)
|
|
694
|
-
- Rides (book ride)
|
|
695
|
-
- Photo (search/send photos)
|
|
696
|
-
- VoIP calling
|
|
697
|
-
|
|
698
|
-
### Implementation
|
|
699
|
-
|
|
700
|
-
Requires native module or Hyperloop to implement Intent Extension.
|
|
701
|
-
|
|
702
|
-
**Basic steps**:
|
|
703
|
-
1. Define supported intents in tiapp.xml
|
|
704
|
-
2. Create Intent Extension in Xcode
|
|
705
|
-
3. Handle intents in extension code
|
|
706
|
-
4. Provide UI for Siri interaction
|
|
707
|
-
|
|
708
|
-
### Voice Shortcuts (iOS 12+)
|
|
709
|
-
|
|
710
|
-
Add custom voice shortcuts for app actions:
|
|
711
|
-
|
|
712
|
-
```javascript
|
|
713
|
-
var INVoiceShortcut = Ti.UI.iOS.createINVoiceShortcut({
|
|
714
|
-
phrase: 'Open My Article',
|
|
715
|
-
activityType: 'com.myapp.reading-article',
|
|
716
|
-
userInfo: { articleId: '123' }
|
|
717
|
-
});
|
|
718
|
-
|
|
719
|
-
// Present to user for recording
|
|
720
|
-
INVoiceShortcut.present();
|
|
721
|
-
```
|
|
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
|
|
722
534
|
|
|
723
|
-
##
|
|
535
|
+
## 9. Additional iOS Features
|
|
724
536
|
|
|
725
537
|
### 3D Touch (Force Touch)
|
|
726
538
|
|
|
@@ -1,41 +1,5 @@
|
|
|
1
1
|
# Local Data Sources
|
|
2
2
|
|
|
3
|
-
## Table of Contents
|
|
4
|
-
|
|
5
|
-
- [Local Data Sources](#local-data-sources)
|
|
6
|
-
- [Table of Contents](#table-of-contents)
|
|
7
|
-
- [1. Filesystem Access and Storage](#1-filesystem-access-and-storage)
|
|
8
|
-
- [Modules Overview](#modules-overview)
|
|
9
|
-
- [Storage Locations](#storage-locations)
|
|
10
|
-
- [File Operations](#file-operations)
|
|
11
|
-
- [Getting a File Handle](#getting-a-file-handle)
|
|
12
|
-
- [Writing Files](#writing-files)
|
|
13
|
-
- [Reading Files](#reading-files)
|
|
14
|
-
- [Appending](#appending)
|
|
15
|
-
- [Creating/Copying](#creatingcopying)
|
|
16
|
-
- [Renaming](#renaming)
|
|
17
|
-
- [Deleting](#deleting)
|
|
18
|
-
- [Directory Operations](#directory-operations)
|
|
19
|
-
- [Case Sensitivity Warning](#case-sensitivity-warning)
|
|
20
|
-
- [2. SQLite Database](#2-sqlite-database)
|
|
21
|
-
- [Opening Databases](#opening-databases)
|
|
22
|
-
- [Querying Data](#querying-data)
|
|
23
|
-
- [Parameterized Queries](#parameterized-queries)
|
|
24
|
-
- [Data Modification](#data-modification)
|
|
25
|
-
- [**CRITICAL**: Always Close Connections](#critical-always-close-connections)
|
|
26
|
-
- [3. Properties API (Ti.App.Properties)](#3-properties-api-tiappproperties)
|
|
27
|
-
- [Overview](#overview)
|
|
28
|
-
- [Data Type Methods](#data-type-methods)
|
|
29
|
-
- [Usage Examples](#usage-examples)
|
|
30
|
-
- [Storing Complex Objects as JSON](#storing-complex-objects-as-json)
|
|
31
|
-
- [Platform Storage](#platform-storage)
|
|
32
|
-
- [4. Advanced Data Manipulation (Buffer, Codec, and Streams)](#4-advanced-data-manipulation-buffer-codec-and-streams)
|
|
33
|
-
- [5. Choosing a Persistence Strategy](#5-choosing-a-persistence-strategy)
|
|
34
|
-
- [Decision Guide](#decision-guide)
|
|
35
|
-
- [Best Practices](#best-practices)
|
|
36
|
-
|
|
37
|
-
---
|
|
38
|
-
|
|
39
3
|
## 1. Filesystem Access and Storage
|
|
40
4
|
|
|
41
5
|
### Modules Overview
|
|
@@ -140,6 +104,14 @@ Android and Mobile Web use case-sensitive filesystems. File names referenced in
|
|
|
140
104
|
|
|
141
105
|
## 2. SQLite Database
|
|
142
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
|
+
|
|
143
115
|
### Opening Databases
|
|
144
116
|
|
|
145
117
|
```javascript
|
|
@@ -188,7 +160,8 @@ db.execute(`INSERT INTO users (name, age) VALUES ('${name}', ${age})`);
|
|
|
188
160
|
```javascript
|
|
189
161
|
// INSERT
|
|
190
162
|
db.execute('INSERT INTO users (name, age) VALUES (?, ?)', ['Jane', 30]);
|
|
191
|
-
const lastId = db.lastInsertRowId;
|
|
163
|
+
const lastId = db.lastInsertRowId; // ID of the last inserted row
|
|
164
|
+
const affected = db.rowsAffected; // number of rows changed by last statement
|
|
192
165
|
|
|
193
166
|
// UPDATE
|
|
194
167
|
db.execute('UPDATE users SET age = ? WHERE name = ?', [31, 'Jane']);
|
|
@@ -198,6 +171,41 @@ const rowsAffected = db.rowsAffected;
|
|
|
198
171
|
db.execute('DELETE FROM users WHERE age < ?', [18]);
|
|
199
172
|
```
|
|
200
173
|
|
|
174
|
+
### Transactions for Batch Operations
|
|
175
|
+
Use transactions to dramatically improve performance for multiple inserts/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` — can only `ADD COLUMN` or `RENAME TABLE`, cannot drop/modify columns
|
|
196
|
+
- No built-in referential integrity (foreign key enforcement) — enable with `PRAGMA foreign_keys = ON`
|
|
197
|
+
- No native boolean type — use INTEGER (0/1)
|
|
198
|
+
|
|
199
|
+
### Disable iCloud Backup for Databases (iOS)
|
|
200
|
+
iOS automatically backs up databases to iCloud. For large or recreatable databases, disable this:
|
|
201
|
+
```javascript
|
|
202
|
+
const db = Ti.Database.open('mydb');
|
|
203
|
+
db.file.setRemoteBackup(false);
|
|
204
|
+
db.close();
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
> **Important**: Apple may reject apps that back up large recreatable data to iCloud.
|
|
208
|
+
|
|
201
209
|
### **CRITICAL**: Always Close Connections
|
|
202
210
|
|
|
203
211
|
```javascript
|
|
@@ -1,33 +1,5 @@
|
|
|
1
1
|
# Location Services and Maps
|
|
2
2
|
|
|
3
|
-
## Table of Contents
|
|
4
|
-
|
|
5
|
-
- [Location Services and Maps](#location-services-and-maps)
|
|
6
|
-
- [Table of Contents](#table-of-contents)
|
|
7
|
-
- [1. GPS Position Tracking](#1-gps-position-tracking)
|
|
8
|
-
- [FusedLocationProvider (Android)](#fusedlocationprovider-android)
|
|
9
|
-
- [Accuracy Configuration](#accuracy-configuration)
|
|
10
|
-
- [Android Configuration Modes](#android-configuration-modes)
|
|
11
|
-
- [One-Time Position](#one-time-position)
|
|
12
|
-
- [Continuous Monitoring](#continuous-monitoring)
|
|
13
|
-
- [Lifecycle Management (Critical for Battery)](#lifecycle-management-critical-for-battery)
|
|
14
|
-
- [Compass/Heading](#compassheading)
|
|
15
|
-
- [Geocoding](#geocoding)
|
|
16
|
-
- [Forward Geocoding (Address → Coordinates)](#forward-geocoding-address--coordinates)
|
|
17
|
-
- [Reverse Geocoding (Coordinates → Places)](#reverse-geocoding-coordinates--places)
|
|
18
|
-
- [2. Native Maps (Platform-Specific Details)](#2-native-maps-platform-specific-details)
|
|
19
|
-
- [Common Map Patterns](#common-map-patterns)
|
|
20
|
-
- [Add Annotation After Map Creation](#add-annotation-after-map-creation)
|
|
21
|
-
- [Remove Annotation](#remove-annotation)
|
|
22
|
-
- [Remove All Annotations](#remove-all-annotations)
|
|
23
|
-
- [Select Annotation Programmatically](#select-annotation-programmatically)
|
|
24
|
-
- [Deselect Annotation](#deselect-annotation)
|
|
25
|
-
- [Set Region with Animation](#set-region-with-animation)
|
|
26
|
-
- [Zoom to Annotations](#zoom-to-annotations)
|
|
27
|
-
- [Best Practices](#best-practices)
|
|
28
|
-
|
|
29
|
-
---
|
|
30
|
-
|
|
31
3
|
## 1. GPS Position Tracking
|
|
32
4
|
|
|
33
5
|
### FusedLocationProvider (Android)
|
|
@@ -51,6 +23,8 @@ Ti.Geolocation.headingFilter = 5; // degrees - only fire if heading changed X
|
|
|
51
23
|
Ti.Geolocation.preferredProvider = Ti.Geolocation.PROVIDER_GPS; // or PROVIDER_NETWORK
|
|
52
24
|
```
|
|
53
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. Therefore `Ti.Geolocation.locationServicesEnabled` may always return `true` on these devices.
|
|
27
|
+
|
|
54
28
|
### Android Configuration Modes
|
|
55
29
|
|
|
56
30
|
**Simple Mode** (ACCURACY_HIGH/LOW):
|
|
@@ -69,6 +43,34 @@ Ti.Geolocation.Android.addLocationProvider(providerGps);
|
|
|
69
43
|
Ti.Geolocation.Android.manualMode = true;
|
|
70
44
|
```
|
|
71
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" even if your app requests "Always".
|
|
73
|
+
|
|
72
74
|
### One-Time Position
|
|
73
75
|
|
|
74
76
|
```javascript
|
|
@@ -147,6 +149,12 @@ Ti.Geolocation.addEventListener('heading', (e) => {
|
|
|
147
149
|
});
|
|
148
150
|
```
|
|
149
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
|
+
|
|
150
158
|
---
|
|
151
159
|
|
|
152
160
|
## Geocoding
|
|
@@ -161,6 +169,17 @@ Ti.Geolocation.forwardGeocoder('440 Bernardo Ave Mountain View CA', (e) => {
|
|
|
161
169
|
});
|
|
162
170
|
```
|
|
163
171
|
|
|
172
|
+
**With error handling:**
|
|
173
|
+
```javascript
|
|
174
|
+
Ti.Geolocation.forwardGeocoder('1600 Amphitheatre Pkwy, Mountain View, CA', (e) => {
|
|
175
|
+
if (!e.success || e.error) {
|
|
176
|
+
Ti.API.error(`Geocoding failed: ${e.error}`);
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
Ti.API.info(`Lat: ${e.latitude}, Lng: ${e.longitude}`);
|
|
180
|
+
});
|
|
181
|
+
```
|
|
182
|
+
|
|
164
183
|
### Reverse Geocoding (Coordinates → Places)
|
|
165
184
|
|
|
166
185
|
```javascript
|