@maccesar/titools 2.2.3 → 2.2.8
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 +28 -27
- package/lib/commands/update.js +80 -101
- package/package.json +1 -1
- package/skills/alloy-guides/SKILL.md +31 -31
- package/skills/alloy-guides/references/CONCEPTS.md +3 -3
- package/skills/alloy-guides/references/CONTROLLERS.md +1 -1
- package/skills/alloy-guides/references/MODELS.md +6 -6
- package/skills/alloy-guides/references/VIEWS_WITHOUT_CONTROLLERS.md +1 -1
- package/skills/alloy-guides/references/VIEWS_XML.md +1 -1
- package/skills/alloy-guides/references/WIDGETS.md +1 -1
- package/skills/alloy-howtos/SKILL.md +27 -27
- package/skills/alloy-howtos/references/best_practices.md +9 -9
- package/skills/alloy-howtos/references/cli_reference.md +14 -14
- package/skills/alloy-howtos/references/config_files.md +16 -16
- package/skills/alloy-howtos/references/custom_tags.md +16 -16
- package/skills/alloy-howtos/references/debugging_troubleshooting.md +11 -15
- package/skills/alloy-howtos/references/samples.md +19 -19
- package/skills/purgetss/SKILL.md +11 -1
- package/skills/purgetss/references/animation-system.md +1 -1
- package/skills/purgetss/references/cli-commands.md +3 -3
- package/skills/purgetss/references/customization-deep-dive.md +1 -1
- package/skills/purgetss/references/dynamic-component-creation.md +1 -1
- package/skills/purgetss/references/icon-fonts.md +4 -0
- package/skills/purgetss/references/installation-setup.md +8 -1
- package/skills/purgetss/references/migration-guide.md +4 -0
- package/skills/purgetss/references/tikit-components.md +193 -204
- package/skills/purgetss/references/ui-ux-design.md +1 -1
- package/skills/ti-expert/SKILL.md +78 -118
- package/skills/ti-expert/references/alloy-builtins.md +18 -18
- package/skills/ti-expert/references/alloy-structure.md +21 -21
- package/skills/ti-expert/references/anti-patterns.md +15 -15
- package/skills/ti-expert/references/cli-expert.md +15 -15
- package/skills/ti-expert/references/code-conventions.md +38 -38
- package/skills/ti-expert/references/contracts.md +8 -8
- package/skills/ti-expert/references/controller-patterns.md +14 -14
- package/skills/ti-expert/references/error-handling.md +11 -11
- package/skills/ti-expert/references/examples.md +12 -12
- package/skills/ti-expert/references/migration-patterns.md +24 -24
- package/skills/ti-expert/references/patterns.md +10 -10
- package/skills/ti-expert/references/performance-listview.md +16 -16
- package/skills/ti-expert/references/performance-optimization.md +41 -41
- package/skills/ti-expert/references/security-device.md +22 -22
- package/skills/ti-expert/references/security-fundamentals.md +19 -19
- package/skills/ti-expert/references/state-management.md +33 -33
- package/skills/ti-expert/references/testing-e2e-ci.md +25 -25
- package/skills/ti-expert/references/testing-unit.md +24 -24
- package/skills/ti-expert/references/theming.md +15 -15
- package/skills/ti-guides/SKILL.md +58 -60
- package/skills/ti-guides/references/advanced-data-and-images.md +33 -33
- package/skills/ti-guides/references/android-manifest.md +15 -15
- package/skills/ti-guides/references/app-distribution.md +70 -166
- package/skills/ti-guides/references/application-frameworks.md +96 -114
- package/skills/ti-guides/references/cli-reference.md +294 -294
- package/skills/ti-guides/references/coding-best-practices.md +42 -33
- package/skills/ti-guides/references/commonjs-advanced.md +57 -51
- package/skills/ti-guides/references/hello-world.md +36 -36
- package/skills/ti-guides/references/hyperloop-native-access.md +66 -66
- package/skills/ti-guides/references/javascript-primer.md +83 -101
- package/skills/ti-guides/references/reserved-words.md +9 -9
- package/skills/ti-guides/references/resources.md +75 -83
- package/skills/ti-guides/references/style-and-conventions.md +35 -28
- package/skills/ti-guides/references/tiapp-config.md +110 -74
- package/skills/ti-howtos/SKILL.md +88 -92
- package/skills/ti-howtos/references/android-platform-deep-dives.md +104 -104
- package/skills/ti-howtos/references/automation-fastlane-appium.md +39 -39
- package/skills/ti-howtos/references/buffer-codec-streams.md +60 -60
- package/skills/ti-howtos/references/cross-platform-development.md +115 -136
- package/skills/ti-howtos/references/debugging-profiling.md +167 -181
- package/skills/ti-howtos/references/extending-titanium.md +121 -121
- package/skills/ti-howtos/references/google-maps-v2.md +84 -82
- package/skills/ti-howtos/references/ios-map-kit.md +65 -60
- package/skills/ti-howtos/references/ios-platform-deep-dives.md +123 -123
- package/skills/ti-howtos/references/local-data-sources.md +79 -78
- package/skills/ti-howtos/references/location-and-maps.md +116 -120
- package/skills/ti-howtos/references/media-apis.md +87 -86
- package/skills/ti-howtos/references/notification-services.md +250 -260
- package/skills/ti-howtos/references/remote-data-sources.md +98 -93
- package/skills/ti-howtos/references/tutorials.md +226 -216
- package/skills/ti-howtos/references/using-modules.md +73 -102
- package/skills/ti-howtos/references/web-content-integration.md +101 -103
- package/skills/ti-howtos/references/webpack-build-pipeline.md +52 -44
- package/skills/ti-ui/SKILL.md +85 -85
- package/skills/ti-ui/references/accessibility-deep-dive.md +57 -57
- package/skills/ti-ui/references/animation-and-matrices.md +79 -79
- package/skills/ti-ui/references/application-structures.md +96 -99
- package/skills/ti-ui/references/custom-fonts-styling.md +99 -99
- package/skills/ti-ui/references/event-handling.md +58 -58
- package/skills/ti-ui/references/gestures.md +62 -64
- package/skills/ti-ui/references/icons-and-splash-screens.md +88 -88
- package/skills/ti-ui/references/layouts-and-positioning.md +87 -97
- package/skills/ti-ui/references/listviews-and-performance.md +107 -107
- package/skills/ti-ui/references/orientation.md +87 -88
- package/skills/ti-ui/references/platform-ui-android.md +87 -81
- package/skills/ti-ui/references/platform-ui-ios.md +63 -61
- package/skills/ti-ui/references/scrolling-views.md +29 -29
- package/skills/ti-ui/references/tableviews.md +56 -56
|
@@ -1,17 +1,16 @@
|
|
|
1
|
-
# Titanium SDK
|
|
1
|
+
# Titanium SDK tutorials
|
|
2
2
|
|
|
3
|
-
Practical tutorials and guides for building real
|
|
3
|
+
Practical tutorials and guides for building real Titanium applications.
|
|
4
4
|
|
|
5
|
-
## RESTe - API
|
|
5
|
+
## RESTe - API integration
|
|
6
6
|
|
|
7
7
|
### Overview
|
|
8
|
+
RESTe by Jason Kneen is a module for consuming web APIs in Titanium.
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
**Benefits:**
|
|
10
|
+
Benefits:
|
|
12
11
|
- Eliminates boilerplate HTTP code
|
|
13
12
|
- Auto-generates JavaScript API from config
|
|
14
|
-
- Supports Backbone.js
|
|
13
|
+
- Supports Backbone.js models and collections in Alloy
|
|
15
14
|
- Works with any API (not just REST)
|
|
16
15
|
|
|
17
16
|
### Installation
|
|
@@ -20,108 +19,113 @@ Practical tutorials and guides for building real-world Titanium applications.
|
|
|
20
19
|
npm install reste
|
|
21
20
|
```
|
|
22
21
|
|
|
23
|
-
### Basic
|
|
22
|
+
### Basic configuration
|
|
23
|
+
|
|
24
|
+
`alloy.js`:
|
|
24
25
|
|
|
25
|
-
**alloy.js:**
|
|
26
26
|
```javascript
|
|
27
|
-
const reste = require(
|
|
27
|
+
const reste = require('reste');
|
|
28
28
|
const api = new reste();
|
|
29
29
|
|
|
30
30
|
api.config({
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
31
|
+
debug: true, // Console logging
|
|
32
|
+
errorsAsObjects: true, // Return errors as objects (1.4.5+) - breaks apps written for 1.4.4 that handle errors differently
|
|
33
|
+
autoValidateParams: false, // Throw error if params missing
|
|
34
|
+
validatesSecureCertificate: false,
|
|
35
|
+
timeout: 4000,
|
|
36
|
+
url: 'https://api.example.com/', // Base URL
|
|
37
|
+
requestHeaders: {
|
|
38
|
+
'X-API-Key': 'your-key-here',
|
|
39
|
+
'Content-Type': 'application/json'
|
|
40
|
+
},
|
|
41
|
+
methods: [
|
|
42
|
+
{
|
|
43
|
+
name: 'getVideos',
|
|
44
|
+
get: 'videos'
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
name: 'getVideoById',
|
|
48
|
+
get: 'videos/'
|
|
40
49
|
},
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
get: "videos"
|
|
45
|
-
},
|
|
46
|
-
{
|
|
47
|
-
name: "getVideoById",
|
|
48
|
-
get: "videos/"
|
|
49
|
-
},
|
|
50
|
-
{
|
|
51
|
-
name: "createVideo",
|
|
52
|
-
post: "videos"
|
|
53
|
-
}
|
|
54
|
-
],
|
|
55
|
-
onError: (e, retry) => {
|
|
56
|
-
const dialog = Ti.UI.createAlertDialog({
|
|
57
|
-
title: "Connection error",
|
|
58
|
-
message: "Check your network and retry.",
|
|
59
|
-
buttonNames: ['Retry']
|
|
60
|
-
});
|
|
61
|
-
dialog.addEventListener("click", () => {
|
|
62
|
-
retry();
|
|
63
|
-
});
|
|
64
|
-
dialog.show();
|
|
50
|
+
{
|
|
51
|
+
name: 'createVideo',
|
|
52
|
+
post: 'videos'
|
|
65
53
|
}
|
|
54
|
+
],
|
|
55
|
+
onError: (e, retry) => {
|
|
56
|
+
const dialog = Ti.UI.createAlertDialog({
|
|
57
|
+
title: 'Connection error',
|
|
58
|
+
message: 'Check your network and retry.',
|
|
59
|
+
buttonNames: ['Retry']
|
|
60
|
+
});
|
|
61
|
+
dialog.addEventListener('click', () => {
|
|
62
|
+
retry();
|
|
63
|
+
});
|
|
64
|
+
dialog.show();
|
|
65
|
+
}
|
|
66
66
|
});
|
|
67
67
|
```
|
|
68
68
|
|
|
69
69
|
### Usage
|
|
70
70
|
|
|
71
|
-
|
|
71
|
+
Auto-generated methods:
|
|
72
|
+
|
|
72
73
|
```javascript
|
|
73
74
|
// Fetch single video
|
|
74
75
|
api.getVideoById({
|
|
75
|
-
|
|
76
|
+
videoId: 'abc123'
|
|
76
77
|
}, (video) => {
|
|
77
|
-
|
|
78
|
+
Ti.API.info(`Got video: ${video.title}`);
|
|
78
79
|
});
|
|
79
80
|
|
|
80
81
|
// Create video
|
|
81
82
|
api.createVideo({
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
83
|
+
body: {
|
|
84
|
+
title: 'My Video',
|
|
85
|
+
categoryId: 2
|
|
86
|
+
}
|
|
86
87
|
}, (response) => {
|
|
87
|
-
|
|
88
|
+
Ti.API.info(`Created: ${response.id}`);
|
|
88
89
|
});
|
|
89
90
|
```
|
|
90
91
|
|
|
91
|
-
### Backbone.js
|
|
92
|
+
### Backbone.js models and collections
|
|
93
|
+
|
|
94
|
+
Configuration:
|
|
92
95
|
|
|
93
|
-
**Configuration:**
|
|
94
96
|
```javascript
|
|
95
97
|
api.config({
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
}]
|
|
98
|
+
// ... other config
|
|
99
|
+
models: [{
|
|
100
|
+
name: 'video',
|
|
101
|
+
id: 'objectId', // Primary key field
|
|
102
|
+
read: 'getVideo',
|
|
103
|
+
create: 'createVideo',
|
|
104
|
+
update: 'updateVideo',
|
|
105
|
+
delete: 'deleteVideo',
|
|
106
|
+
collections: [{
|
|
107
|
+
name: 'videos',
|
|
108
|
+
content: 'results', // Array wrapper in response
|
|
109
|
+
read: 'getVideos'
|
|
109
110
|
}]
|
|
111
|
+
}]
|
|
110
112
|
});
|
|
111
113
|
```
|
|
112
114
|
|
|
113
|
-
|
|
115
|
+
In an Alloy controller:
|
|
116
|
+
|
|
114
117
|
```javascript
|
|
115
118
|
// Fetch collection
|
|
116
119
|
Alloy.Collections.videos.fetch();
|
|
117
120
|
|
|
118
121
|
// Or fetch with parameters
|
|
119
122
|
Alloy.Collections.videos.fetch({
|
|
120
|
-
|
|
123
|
+
data: { category: 'sports' }
|
|
121
124
|
});
|
|
122
125
|
```
|
|
123
126
|
|
|
124
|
-
|
|
127
|
+
In an Alloy view:
|
|
128
|
+
|
|
125
129
|
```xml
|
|
126
130
|
<TableView dataCollection="videos" onClick="selectVideo">
|
|
127
131
|
<TableViewRow model="{objectId}">
|
|
@@ -131,54 +135,57 @@ Alloy.Collections.videos.fetch({
|
|
|
131
135
|
</TableView>
|
|
132
136
|
```
|
|
133
137
|
|
|
134
|
-
### RESTe
|
|
138
|
+
### RESTe advanced features
|
|
139
|
+
|
|
140
|
+
- Caching: built-in response caching
|
|
141
|
+
- Promises: use with `q.js` for Promise-based API calls
|
|
142
|
+
- Mock data: create models and collections on the fly for testing
|
|
135
143
|
|
|
136
|
-
- **Caching**: Built-in response caching support
|
|
137
|
-
- **Promises**: Use with `q.js` for Promise-based API calls
|
|
138
|
-
- **Mock data**: Create models/collections on-the-fly for testing:
|
|
139
144
|
```javascript
|
|
140
145
|
const mockUser = api.createModel('users', {
|
|
141
|
-
|
|
142
|
-
|
|
146
|
+
id: 1,
|
|
147
|
+
name: 'Test User'
|
|
143
148
|
});
|
|
144
149
|
```
|
|
145
|
-
- **Backbone.js binding**: Use RESTe models directly with Alloy data binding without `<Model>` tags
|
|
146
150
|
|
|
147
|
-
|
|
151
|
+
- Backbone.js binding: use RESTe models directly with Alloy data binding without `<Model>` tags
|
|
152
|
+
|
|
153
|
+
### Important notes
|
|
148
154
|
|
|
149
|
-
-
|
|
150
|
-
- RESTe creates models
|
|
151
|
-
- Use data
|
|
152
|
-
-
|
|
155
|
+
- Do not use `<Model>` or `<Collection>` tags with RESTe
|
|
156
|
+
- RESTe creates models and collections automatically
|
|
157
|
+
- Use data binding like normal Alloy collections
|
|
158
|
+
- Useful for mocking data during UI development
|
|
153
159
|
|
|
154
|
-
### Mock
|
|
160
|
+
### Mock data (on the fly)
|
|
155
161
|
|
|
156
162
|
```javascript
|
|
157
163
|
// Create model without API
|
|
158
164
|
const videoModel = api.createModel('video', {
|
|
159
|
-
|
|
160
|
-
|
|
165
|
+
title: 'Test Video',
|
|
166
|
+
description: 'Mock data'
|
|
161
167
|
});
|
|
162
168
|
|
|
163
169
|
// Create collection
|
|
164
170
|
const videoCollection = api.createCollection('videos', [
|
|
165
|
-
|
|
166
|
-
|
|
171
|
+
{ title: 'Video 1' },
|
|
172
|
+
{ title: 'Video 2' }
|
|
167
173
|
]);
|
|
168
174
|
```
|
|
169
175
|
|
|
170
176
|
### Resources
|
|
171
177
|
|
|
172
|
-
-
|
|
173
|
-
-
|
|
178
|
+
- npm: https://www.npmjs.com/package/reste
|
|
179
|
+
- GitHub: https://github.com/jasonkneen/reste
|
|
174
180
|
|
|
175
181
|
---
|
|
176
182
|
|
|
177
|
-
## Camera
|
|
183
|
+
## Camera app tutorial
|
|
184
|
+
|
|
185
|
+
### Camera permissions setup
|
|
178
186
|
|
|
179
|
-
|
|
187
|
+
iOS - add to `tiapp.xml` plist:
|
|
180
188
|
|
|
181
|
-
**iOS** — add to tiapp.xml plist:
|
|
182
189
|
```xml
|
|
183
190
|
<key>NSCameraUsageDescription</key>
|
|
184
191
|
<string>This app needs camera access to take photos</string>
|
|
@@ -186,96 +193,98 @@ const videoCollection = api.createCollection('videos', [
|
|
|
186
193
|
<string>This app needs microphone access for video recording</string>
|
|
187
194
|
```
|
|
188
195
|
|
|
189
|
-
|
|
196
|
+
Android: camera permissions are automatically added by the build system when you use `Ti.Media.showCamera()`.
|
|
190
197
|
|
|
191
198
|
Always check permissions at runtime:
|
|
199
|
+
|
|
192
200
|
```javascript
|
|
193
201
|
if (!Ti.Media.hasCameraPermissions()) {
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
202
|
+
Ti.Media.requestCameraPermissions((e) => {
|
|
203
|
+
if (e.success) openCamera();
|
|
204
|
+
});
|
|
197
205
|
} else {
|
|
198
|
-
|
|
206
|
+
openCamera();
|
|
199
207
|
}
|
|
200
208
|
```
|
|
201
209
|
|
|
202
|
-
### Basic
|
|
210
|
+
### Basic camera access
|
|
203
211
|
|
|
204
212
|
```javascript
|
|
205
213
|
// Show camera
|
|
206
214
|
Ti.Media.showCamera({
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
215
|
+
success: (event) => {
|
|
216
|
+
const image = event.media;
|
|
217
|
+
if (event.mediaType === Ti.Media.MEDIA_TYPE_PHOTO) {
|
|
218
|
+
// Use the photo
|
|
219
|
+
const imageView = Ti.UI.createImageView({
|
|
220
|
+
image: image,
|
|
221
|
+
width: 300,
|
|
222
|
+
height: 300
|
|
223
|
+
});
|
|
224
|
+
win.add(imageView);
|
|
225
|
+
}
|
|
226
|
+
},
|
|
227
|
+
cancel: () => {
|
|
228
|
+
Ti.API.info('Camera canceled');
|
|
229
|
+
},
|
|
230
|
+
error: (error) => {
|
|
231
|
+
const a = Ti.UI.createAlertDialog({ title: 'Camera Error' });
|
|
232
|
+
if (error.code === Ti.Media.NO_CAMERA) {
|
|
233
|
+
a.setMessage('Device does not have camera');
|
|
234
|
+
} else {
|
|
235
|
+
a.setMessage(`Unexpected error: ${error.code}`);
|
|
236
|
+
}
|
|
237
|
+
a.show();
|
|
238
|
+
},
|
|
239
|
+
saveToPhotoGallery: true,
|
|
240
|
+
allowEditing: true,
|
|
241
|
+
mediaTypes: [Ti.Media.MEDIA_TYPE_PHOTO],
|
|
242
|
+
videoQuality: Ti.Media.QUALITY_HIGH
|
|
235
243
|
});
|
|
236
244
|
```
|
|
237
245
|
|
|
238
|
-
### Photo
|
|
246
|
+
### Photo gallery
|
|
239
247
|
|
|
240
248
|
```javascript
|
|
241
249
|
// Open photo gallery
|
|
242
250
|
Ti.Media.openPhotoGallery({
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
251
|
+
success: (event) => {
|
|
252
|
+
const image = event.media;
|
|
253
|
+
// Process selected image
|
|
254
|
+
},
|
|
255
|
+
cancel: () => {
|
|
256
|
+
// User canceled
|
|
257
|
+
},
|
|
258
|
+
error: (error) => {
|
|
259
|
+
// Handle error
|
|
260
|
+
},
|
|
261
|
+
mediaTypes: [Ti.Media.MEDIA_TYPE_PHOTO]
|
|
254
262
|
});
|
|
255
263
|
```
|
|
256
264
|
|
|
257
|
-
### Saving
|
|
265
|
+
### Saving images
|
|
258
266
|
|
|
259
267
|
```javascript
|
|
260
268
|
// Save to gallery
|
|
261
269
|
Ti.Media.saveToPhotoGallery({
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
270
|
+
media: myImageBlob,
|
|
271
|
+
success: () => {
|
|
272
|
+
alert('Saved!');
|
|
273
|
+
},
|
|
274
|
+
error: (e) => {
|
|
275
|
+
alert(`Error: ${e.error}`);
|
|
276
|
+
}
|
|
269
277
|
});
|
|
270
278
|
```
|
|
271
279
|
|
|
272
280
|
---
|
|
273
281
|
|
|
274
|
-
## Geolocation
|
|
282
|
+
## Geolocation tutorial
|
|
283
|
+
|
|
284
|
+
### Geolocation permissions setup
|
|
275
285
|
|
|
276
|
-
|
|
286
|
+
iOS - add to `tiapp.xml` plist:
|
|
277
287
|
|
|
278
|
-
**iOS** — add to tiapp.xml plist:
|
|
279
288
|
```xml
|
|
280
289
|
<key>NSLocationWhenInUseUsageDescription</key>
|
|
281
290
|
<string>We need your location to show nearby places</string>
|
|
@@ -283,7 +292,8 @@ Ti.Media.saveToPhotoGallery({
|
|
|
283
292
|
<string>We need your location for background tracking</string>
|
|
284
293
|
```
|
|
285
294
|
|
|
286
|
-
|
|
295
|
+
Android - add to `tiapp.xml` manifest:
|
|
296
|
+
|
|
287
297
|
```xml
|
|
288
298
|
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
|
|
289
299
|
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
|
|
@@ -292,59 +302,59 @@ Ti.Media.saveToPhotoGallery({
|
|
|
292
302
|
Runtime permission check:
|
|
293
303
|
```javascript
|
|
294
304
|
if (!Ti.Geolocation.hasLocationPermissions(Ti.Geolocation.AUTHORIZATION_WHEN_IN_USE)) {
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
305
|
+
Ti.Geolocation.requestLocationPermissions(Ti.Geolocation.AUTHORIZATION_WHEN_IN_USE, (e) => {
|
|
306
|
+
if (e.success) startTracking();
|
|
307
|
+
});
|
|
298
308
|
}
|
|
299
309
|
```
|
|
300
310
|
|
|
301
|
-
### Basic
|
|
311
|
+
### Basic location
|
|
302
312
|
|
|
303
313
|
```javascript
|
|
304
314
|
// One-time location
|
|
305
315
|
if (Ti.Geolocation.locationServicesEnabled) {
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
316
|
+
Ti.Geolocation.getCurrentPosition((e) => {
|
|
317
|
+
if (!e.error) {
|
|
318
|
+
Ti.API.info(`Lat: ${e.coords.latitude}`);
|
|
319
|
+
Ti.API.info(`Lon: ${e.coords.longitude}`);
|
|
320
|
+
}
|
|
321
|
+
});
|
|
312
322
|
}
|
|
313
323
|
```
|
|
314
324
|
|
|
315
|
-
### Continuous
|
|
325
|
+
### Continuous tracking
|
|
316
326
|
|
|
317
327
|
```javascript
|
|
318
328
|
// Configure accuracy
|
|
319
329
|
Ti.Geolocation.accuracy = Ti.Geolocation.ACCURACY_BEST;
|
|
320
|
-
Ti.Geolocation.distanceFilter = 10;
|
|
330
|
+
Ti.Geolocation.distanceFilter = 10; // Meters
|
|
321
331
|
|
|
322
332
|
// Track location changes
|
|
323
333
|
Ti.Geolocation.addEventListener('location', (e) => {
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
334
|
+
if (!e.error) {
|
|
335
|
+
const lat = e.coords.latitude;
|
|
336
|
+
const lon = e.coords.longitude;
|
|
337
|
+
updateMap(lat, lon);
|
|
338
|
+
}
|
|
329
339
|
});
|
|
330
340
|
```
|
|
331
341
|
|
|
332
|
-
### Reverse
|
|
342
|
+
### Reverse geocoding
|
|
333
343
|
|
|
334
344
|
```javascript
|
|
335
345
|
// Coordinates to address
|
|
336
346
|
Ti.Geolocation.reverseGeocoder(37.389569, -122.050212, (e) => {
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
347
|
+
if (e.success) {
|
|
348
|
+
e.places.forEach((place) => {
|
|
349
|
+
Ti.API.info(place.address);
|
|
350
|
+
Ti.API.info(place.city);
|
|
351
|
+
Ti.API.info(place.country);
|
|
352
|
+
});
|
|
353
|
+
}
|
|
344
354
|
});
|
|
345
355
|
```
|
|
346
356
|
|
|
347
|
-
### Accuracy
|
|
357
|
+
### Accuracy tuning
|
|
348
358
|
|
|
349
359
|
```javascript
|
|
350
360
|
// iOS: minimum distance before firing event
|
|
@@ -355,40 +365,40 @@ Ti.Geolocation.distanceFilter = 10; // meters
|
|
|
355
365
|
|
|
356
366
|
// Android: custom location rules
|
|
357
367
|
Ti.Geolocation.Android.addLocationRule(Ti.Geolocation.Android.createLocationRule({
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
368
|
+
provider: Ti.Geolocation.Android.PROVIDER_GPS,
|
|
369
|
+
accuracy: 100, // meters
|
|
370
|
+
minAge: 1000 // milliseconds
|
|
361
371
|
}));
|
|
362
372
|
```
|
|
363
373
|
|
|
364
374
|
---
|
|
365
375
|
|
|
366
|
-
## Alloy
|
|
376
|
+
## Alloy controller chaining
|
|
367
377
|
|
|
368
|
-
### Pattern
|
|
378
|
+
### Pattern overview
|
|
369
379
|
|
|
370
380
|
Chain controller methods to pass data between Alloy controllers in a clean, maintainable way.
|
|
371
381
|
|
|
372
|
-
### Basic
|
|
382
|
+
### Basic pattern
|
|
373
383
|
|
|
374
|
-
|
|
384
|
+
Parent controller:
|
|
375
385
|
```javascript
|
|
376
386
|
const childController = Alloy.createController('child', {
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
387
|
+
data: someData,
|
|
388
|
+
onAction: (result) => {
|
|
389
|
+
// Handle result from child
|
|
390
|
+
Ti.API.info(`Child returned: ${result}`);
|
|
391
|
+
}
|
|
382
392
|
});
|
|
383
393
|
|
|
384
394
|
childController.on('customEvent', (data) => {
|
|
385
|
-
|
|
395
|
+
// Handle child events
|
|
386
396
|
});
|
|
387
397
|
|
|
388
398
|
childController.open();
|
|
389
399
|
```
|
|
390
400
|
|
|
391
|
-
|
|
401
|
+
Child controller:
|
|
392
402
|
```javascript
|
|
393
403
|
const args = arguments[0] || {};
|
|
394
404
|
|
|
@@ -397,27 +407,27 @@ const parentData = args.data;
|
|
|
397
407
|
|
|
398
408
|
// Call parent callback
|
|
399
409
|
function notifyParent(result) {
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
410
|
+
if (args.onAction) {
|
|
411
|
+
args.onAction(result);
|
|
412
|
+
}
|
|
403
413
|
}
|
|
404
414
|
|
|
405
415
|
// Trigger parent events
|
|
406
416
|
function triggerEvent(data) {
|
|
407
|
-
|
|
417
|
+
$.trigger('customEvent', data);
|
|
408
418
|
}
|
|
409
419
|
```
|
|
410
420
|
|
|
411
421
|
### Benefits
|
|
412
422
|
|
|
413
|
-
-
|
|
414
|
-
-
|
|
415
|
-
-
|
|
416
|
-
-
|
|
423
|
+
- Clean separation. Controllers remain independent.
|
|
424
|
+
- Reusable. Child works in different contexts.
|
|
425
|
+
- Testable. You can mock parent callbacks.
|
|
426
|
+
- Maintainable. Clear data flow.
|
|
417
427
|
|
|
418
428
|
---
|
|
419
429
|
|
|
420
|
-
## Build
|
|
430
|
+
## Build automation with Fastlane
|
|
421
431
|
|
|
422
432
|
### Overview
|
|
423
433
|
|
|
@@ -435,7 +445,7 @@ sudo gem install fastlane -NV
|
|
|
435
445
|
|
|
436
446
|
### Basic Fastfile
|
|
437
447
|
|
|
438
|
-
|
|
448
|
+
`fastlane/Fastfile`:
|
|
439
449
|
```ruby
|
|
440
450
|
platform :ios do
|
|
441
451
|
desc "Build and deploy to TestFlight"
|
|
@@ -488,9 +498,9 @@ fastlane android build
|
|
|
488
498
|
fastlane android beta
|
|
489
499
|
```
|
|
490
500
|
|
|
491
|
-
### Advanced
|
|
501
|
+
### Advanced options
|
|
492
502
|
|
|
493
|
-
|
|
503
|
+
Multiple environments:
|
|
494
504
|
```ruby
|
|
495
505
|
lane :staging do
|
|
496
506
|
sh "ti build -p ios -T dist --config ../staging.js"
|
|
@@ -501,42 +511,42 @@ lane :production do
|
|
|
501
511
|
end
|
|
502
512
|
```
|
|
503
513
|
|
|
504
|
-
|
|
514
|
+
Environment-specific configs:
|
|
505
515
|
```javascript
|
|
506
516
|
// staging.js
|
|
507
517
|
module.exports = {
|
|
508
|
-
|
|
509
|
-
|
|
518
|
+
apiURL: 'https://staging-api.example.com',
|
|
519
|
+
analyticsEnabled: true
|
|
510
520
|
};
|
|
511
521
|
|
|
512
522
|
// production.js
|
|
513
523
|
module.exports = {
|
|
514
|
-
|
|
515
|
-
|
|
524
|
+
apiURL: 'https://api.example.com',
|
|
525
|
+
analyticsEnabled: true
|
|
516
526
|
};
|
|
517
527
|
```
|
|
518
528
|
|
|
519
529
|
---
|
|
520
530
|
|
|
521
|
-
## Additional
|
|
531
|
+
## Additional tutorials
|
|
522
532
|
|
|
523
|
-
### Alloy
|
|
533
|
+
### Alloy boilerplates
|
|
524
534
|
|
|
525
|
-
See
|
|
535
|
+
See `Titanium_Boilerplates` folder for:
|
|
526
536
|
- TypeScript boilerplates
|
|
527
537
|
- App templates
|
|
528
538
|
- Project scaffolding
|
|
529
539
|
|
|
530
|
-
### Complete
|
|
540
|
+
### Complete sample apps
|
|
531
541
|
|
|
532
|
-
-
|
|
533
|
-
-
|
|
534
|
-
-
|
|
542
|
+
- ToDoList - CommonJS module pattern example
|
|
543
|
+
- Tweetanium - Namespaced pattern example
|
|
544
|
+
- KitchenSink - Complete API reference (built with Alloy)
|
|
535
545
|
|
|
536
546
|
---
|
|
537
547
|
|
|
538
548
|
## Resources
|
|
539
549
|
|
|
540
|
-
-
|
|
541
|
-
-
|
|
542
|
-
-
|
|
550
|
+
- RESTe: https://github.com/jasonkneen/reste
|
|
551
|
+
- Fastlane: https://fastlane.tools
|
|
552
|
+
- Alloy Framework: see alloy-guides skill
|