@maccesar/titools 5.3.0 → 5.4.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/CONTRIBUTING.md +1 -1
- package/README.md +31 -60
- package/agents/ti-pro.md +3 -1
- package/commands/ti-audit.md +6 -4
- package/docs/examples.md +9 -0
- package/docs/knowledge-index.md +2 -1
- package/lib/config.js +1 -0
- package/package.json +1 -1
- package/skills/alloy-guides/SKILL.md +18 -179
- package/skills/alloy-guides/references/CLI_TASKS.md +17 -216
- package/skills/alloy-guides/references/CONCEPTS.md +27 -162
- package/skills/alloy-guides/references/CONTROLLERS.md +38 -236
- package/skills/alloy-guides/references/MODELS.md +47 -723
- package/skills/alloy-guides/references/MODELS_ADVANCED.md +21 -482
- package/skills/alloy-guides/references/VIEWS_DYNAMIC.md +12 -222
- package/skills/alloy-guides/references/VIEWS_STYLES.md +39 -358
- package/skills/alloy-guides/references/VIEWS_XML.md +36 -552
- package/skills/alloy-guides/references/WIDGETS.md +49 -127
- package/skills/alloy-howtos/SKILL.md +14 -169
- package/skills/alloy-howtos/references/best_practices.md +9 -113
- package/skills/alloy-howtos/references/cli_reference.md +18 -223
- package/skills/alloy-howtos/references/config_files.md +50 -133
- package/skills/alloy-howtos/references/debugging_troubleshooting.md +19 -63
- package/skills/alloy-howtos/references/samples.md +21 -138
- package/skills/ti-api/SKILL.md +10 -2
- package/skills/ti-api/references/api-android.md +36 -2
- package/skills/ti-api/references/api-app-platform.md +39 -3
- package/skills/ti-api/references/api-core.md +50 -18
- package/skills/ti-api/references/api-data-network.md +49 -35
- package/skills/ti-api/references/api-media.md +45 -27
- package/skills/ti-api/references/api-services.md +39 -9
- package/skills/ti-api/references/api-ui-android.md +33 -13
- package/skills/ti-api/references/api-ui-extras.md +41 -15
- package/skills/ti-api/references/api-ui-ios-animator.md +42 -26
- package/skills/ti-api/references/api-ui-ios.md +74 -2
- package/skills/ti-api/references/api-ui-lists.md +32 -16
- package/skills/ti-api/references/api-ui-text-input.md +34 -17
- package/skills/ti-api/references/api-ui-views.md +27 -1
- package/skills/ti-api/references/api-ui-windows-navigation.md +32 -18
- package/skills/ti-api/references/api-xml-global.md +66 -16
- package/skills/ti-create-release/SKILL.md +98 -0
- package/skills/ti-create-release/references/module-discovery.md +69 -0
- package/skills/ti-expert/references/adaptive-layouts.md +1 -1
- package/skills/ti-guides/SKILL.md +37 -76
- package/skills/ti-guides/references/advanced-data-and-images.md +122 -126
- package/skills/ti-guides/references/android-manifest.md +24 -83
- package/skills/ti-guides/references/app-distribution.md +31 -377
- package/skills/ti-guides/references/application-frameworks.md +127 -335
- package/skills/ti-guides/references/cli-reference.md +184 -633
- package/skills/ti-guides/references/commonjs-advanced.md +81 -231
- package/skills/ti-guides/references/compatibility-matrix.md +36 -75
- package/skills/ti-guides/references/hello-world.md +95 -70
- package/skills/ti-guides/references/hyperloop-native-access.md +52 -448
- package/skills/ti-guides/references/resources.md +174 -142
- package/skills/ti-guides/references/sdk-release-notes.md +28 -71
- package/skills/ti-guides/references/tiapp-config.md +222 -598
- package/skills/ti-howtos/SKILL.md +23 -123
- package/skills/ti-howtos/references/cross-platform-development.md +119 -324
- package/skills/ti-howtos/references/debugging-profiling.md +77 -445
- package/skills/ti-howtos/references/extending-titanium.md +199 -673
- package/skills/ti-howtos/references/local-data-sources.md +87 -300
- package/skills/ti-howtos/references/location-and-maps.md +86 -213
- package/skills/ti-howtos/references/media-apis.md +103 -223
- package/skills/ti-howtos/references/notification-services.md +86 -506
- package/skills/ti-howtos/references/remote-data-sources.md +97 -316
- package/skills/ti-howtos/references/using-modules.md +77 -133
- package/skills/ti-ui/SKILL.md +28 -143
- package/skills/ti-ui/references/icons-and-splash-screens.md +74 -403
- package/skills/ti-ui/references/layouts-and-positioning.md +175 -402
- package/skills/ti-ui/references/listviews-and-performance.md +100 -703
- package/skills/ti-ui/references/platform-conventions.md +180 -0
- package/skills/ti-ui/references/user-interface.md +43 -0
- package/skills/alloy-guides/references/VIEWS_WITHOUT_CONTROLLERS.md +0 -102
- package/skills/alloy-howtos/references/custom_tags.md +0 -148
- package/skills/ti-guides/references/coding-best-practices.md +0 -149
- package/skills/ti-guides/references/javascript-primer.md +0 -414
- package/skills/ti-guides/references/reserved-words.md +0 -36
- package/skills/ti-guides/references/style-and-conventions.md +0 -103
- package/skills/ti-howtos/references/android-platform-deep-dives.md +0 -624
- package/skills/ti-howtos/references/automation-fastlane-appium.md +0 -96
- package/skills/ti-howtos/references/buffer-codec-streams.md +0 -162
- package/skills/ti-howtos/references/google-maps-v2.md +0 -172
- package/skills/ti-howtos/references/ios-map-kit.md +0 -149
- package/skills/ti-howtos/references/ios-platform-deep-dives.md +0 -611
- package/skills/ti-howtos/references/tutorials.md +0 -565
- package/skills/ti-howtos/references/web-content-integration.md +0 -287
- package/skills/ti-howtos/references/webpack-build-pipeline.md +0 -124
- package/skills/ti-ui/references/accessibility-deep-dive.md +0 -288
- package/skills/ti-ui/references/animation-and-matrices.md +0 -569
- package/skills/ti-ui/references/application-structures.md +0 -626
- package/skills/ti-ui/references/custom-fonts-styling.md +0 -549
- package/skills/ti-ui/references/event-handling.md +0 -401
- package/skills/ti-ui/references/gestures.md +0 -448
- package/skills/ti-ui/references/orientation.md +0 -374
- package/skills/ti-ui/references/platform-ui-android.md +0 -727
- package/skills/ti-ui/references/platform-ui-ios.md +0 -576
- package/skills/ti-ui/references/scrolling-views.md +0 -288
- package/skills/ti-ui/references/tableviews.md +0 -542
|
@@ -1,775 +1,301 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Native modules
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
## Contents
|
|
3
|
+
Based on the [current official guide](https://titaniumsdk.com/docs/extend/modules.md). Examples target released SDK 13.4.1; verify the selected SDK before adopting version-specific behavior.
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
- [4. Hyperloop](#4-hyperloop)
|
|
10
|
-
- [3. Native module development](#3-native-module-development)
|
|
11
|
-
- [Swift Package Manager dependencies (iOS)](#swift-package-manager-dependencies-ios)
|
|
12
|
-
- [4. Choosing between Hyperloop and native modules](#4-choosing-between-hyperloop-and-native-modules)
|
|
13
|
-
- [5. Finding and using third-party modules](#5-finding-and-using-third-party-modules)
|
|
14
|
-
- [Best practices summary](#best-practices-summary)
|
|
5
|
+
A native module wraps platform code and exposes it to JavaScript. Write one
|
|
6
|
+
when the SDK has no API for something the platform does, or when you need a
|
|
7
|
+
native library that has no JavaScript equivalent.
|
|
15
8
|
|
|
16
|
-
|
|
9
|
+
Before you do, check the [modules directory](https://titaniumsdk.com/modules.md) - somebody may already
|
|
10
|
+
have. And check [Hyperloop](https://titaniumsdk.com/docs/extend/hyperloop.md), which reaches native APIs
|
|
11
|
+
directly without a module at all.
|
|
17
12
|
|
|
18
|
-
##
|
|
13
|
+
## Create one
|
|
19
14
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
- Proxy: base class that binds native code with JS and maintains object state
|
|
23
|
-
- Module: special Proxy that defines a namespace (for example, `Ti.UI`). Only one per project is allowed.
|
|
24
|
-
- ViewProxy: Proxy specialized in rendering views; manages the native view lifecycle
|
|
25
|
-
- View: the actual visual representation (for example, a `UIButton` or an Android `UIView`)
|
|
26
|
-
|
|
27
|
-
### Proxy-view relationship
|
|
28
|
-
The `ViewProxy` keeps properties in JS even if the native view does not exist yet. When the view is added to the hierarchy, the Proxy creates the native `View` and applies all accumulated properties.
|
|
29
|
-
|
|
30
|
-
### Threading considerations
|
|
31
|
-
- JavaScript in Titanium is single-threaded. All JS callbacks run on the JS runtime thread.
|
|
32
|
-
- Native modules can use background threads for heavy work, but UI operations must happen on the main/UI thread.
|
|
33
|
-
- On iOS, each Window with a `url` property gets its own JS context and thread. Avoid this pattern.
|
|
34
|
-
- Use `@Kroll.method(runOnUiThread=true)` (Android) to ensure a method runs on the UI thread.
|
|
35
|
-
|
|
36
|
-
### Key Android Kroll annotations
|
|
37
|
-
- `@Kroll.method` - expose a Java method to JavaScript
|
|
38
|
-
- `@Kroll.getProperty` / `@Kroll.setProperty` - expose getter/setter properties
|
|
39
|
-
- `@Kroll.onAppCreate` - annotate a static method to run when the app process starts
|
|
40
|
-
- `propertyAccessors` in `@Kroll.proxy` - auto-generate getter/setter methods
|
|
41
|
-
|
|
42
|
-
## 2. Module debugging in Xcode (iOS)
|
|
43
|
-
|
|
44
|
-
You can debug a native module directly within a Titanium project:
|
|
45
|
-
|
|
46
|
-
1. Open the generated Xcode project at `build/iphone/<App>.xcodeproj`.
|
|
47
|
-
2. Drag the module project (`.xcodeproj`) into the app project in Xcode.
|
|
48
|
-
3. In the app, go to Build Phases → Dependencies and add the module.
|
|
49
|
-
4. In Link Binary With Libraries, add the module's `.a` library or framework.
|
|
50
|
-
5. Set a breakpoint in your native code and launch the app from Xcode.
|
|
51
|
-
|
|
52
|
-
## 3. Upgrading to SDK 9.0.0+ (Android)
|
|
53
|
-
|
|
54
|
-
Titanium 9.0.0 introduced major Android changes:
|
|
55
|
-
|
|
56
|
-
- AndroidX: legacy support libraries are no longer supported. Migrate Java/Kotlin code to AndroidX.
|
|
57
|
-
- Gradle: modules now use Gradle for dependencies. Create `android/build.gradle` instead of copying `.jar` files into `lib/`.
|
|
58
|
-
- Architectures: include `arm64-v8a` and `x86_64` in the manifest.
|
|
59
|
-
|
|
60
|
-
Example module `build.gradle`:
|
|
61
|
-
```gradle
|
|
62
|
-
dependencies {
|
|
63
|
-
implementation 'com.google.android.material:material:1.1.0'
|
|
64
|
-
}
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
## 4. Hyperloop
|
|
68
|
-
|
|
69
|
-
### What is Hyperloop?
|
|
70
|
-
|
|
71
|
-
Hyperloop provides direct JavaScript access to native iOS and Android APIs without writing wrapper code. You can call native methods, create native objects, and interact with platform frameworks directly.
|
|
72
|
-
|
|
73
|
-
### Prerequisites
|
|
74
|
-
|
|
75
|
-
For iOS:
|
|
76
|
-
- Requires `ti.hyperloop` module in `tiapp.xml`
|
|
77
|
-
- Only works with Classic Titanium projects (not Alloy initially, now supported)
|
|
78
|
-
- iOS 7+ deployment target
|
|
79
|
-
|
|
80
|
-
For Android:
|
|
81
|
-
- Requires `ti.hyperloop` module in `tiapp.xml`
|
|
82
|
-
- Android 4.0+ (API level 14+)
|
|
83
|
-
|
|
84
|
-
Enable in `tiapp.xml`:
|
|
85
|
-
```xml
|
|
86
|
-
<modules>
|
|
87
|
-
<module platform="iphone">ti.hyperloop</module>
|
|
88
|
-
<module platform="android">ti.hyperloop</module>
|
|
89
|
-
</modules>
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
### Hyperloop basics
|
|
93
|
-
|
|
94
|
-
Pattern for accessing native APIs:
|
|
95
|
-
```javascript
|
|
96
|
-
const NativeClass = require('path.to.NativeClass');
|
|
97
|
-
const instance = new NativeClass();
|
|
98
|
-
instance.methodName();
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
### iOS Hyperloop examples
|
|
102
|
-
|
|
103
|
-
#### Access iOS frameworks
|
|
104
|
-
|
|
105
|
-
```javascript
|
|
106
|
-
// Access Foundation framework
|
|
107
|
-
const NSString = require('Foundation/NSString');
|
|
108
|
-
const NSMutableString = require('Foundation/NSMutableString');
|
|
109
|
-
|
|
110
|
-
// Create string
|
|
111
|
-
const str = NSString.stringWithString('Hello from Hyperloop');
|
|
112
|
-
Ti.API.info(`Length: ${str.length()}`);
|
|
113
|
-
|
|
114
|
-
// Mutable string
|
|
115
|
-
const mutable = NSMutableString.alloc().initWithString('Hello');
|
|
116
|
-
mutable.appendString(' Hyperloop');
|
|
117
|
-
Ti.API.info(mutable); // "Hello Hyperloop"
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
#### Access UIKit
|
|
121
|
-
|
|
122
|
-
```javascript
|
|
123
|
-
const UIViewController = require('UIKit/UIViewController');
|
|
124
|
-
const UIColor = require('UIKit/UIColor');
|
|
125
|
-
|
|
126
|
-
// Create native view controller
|
|
127
|
-
const controller = UIViewController.alloc().init();
|
|
128
|
-
|
|
129
|
-
// Set background color
|
|
130
|
-
controller.view().setBackgroundColor(
|
|
131
|
-
UIColor.redColor()
|
|
132
|
-
);
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
#### Make native HTTP request
|
|
136
|
-
|
|
137
|
-
```javascript
|
|
138
|
-
const NSURL = require('Foundation/NSURL');
|
|
139
|
-
const NSURLRequest = require('Foundation/NSURLRequest');
|
|
140
|
-
const NSURLSession = require('Foundation/NSURLSession');
|
|
141
|
-
|
|
142
|
-
const url = NSURL.URLWithString('https://api.example.com/data');
|
|
143
|
-
const request = NSURLRequest.requestWithURL(url);
|
|
144
|
-
|
|
145
|
-
const session = NSURLSession.sharedSession();
|
|
146
|
-
const task = session.dataTaskWithRequestCompletionHandler(request, (data, response, error) => {
|
|
147
|
-
if (!error) {
|
|
148
|
-
Ti.API.info('Response received');
|
|
149
|
-
// Process data
|
|
150
|
-
}
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
task.resume();
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
#### Access CoreBluetooth (Bluetooth LE)
|
|
157
|
-
|
|
158
|
-
```javascript
|
|
159
|
-
var CBCentralManager = require('CoreBluetooth/CBCentralManager');
|
|
160
|
-
|
|
161
|
-
var central = CBCentralManager.alloc().initWithDelegateQueueOptions(
|
|
162
|
-
null, // delegate
|
|
163
|
-
null // queue
|
|
164
|
-
);
|
|
165
|
-
|
|
166
|
-
// Check Bluetooth state
|
|
167
|
-
var state = central.state();
|
|
168
|
-
if (state === 5) { // CBCentralManagerStatePoweredOn
|
|
169
|
-
// Start scanning
|
|
170
|
-
central.scanForPeripheralsWithServicesOptions(null, null);
|
|
171
|
-
}
|
|
172
|
-
```
|
|
173
|
-
|
|
174
|
-
#### Access AVFoundation (camera/video)
|
|
175
|
-
|
|
176
|
-
```javascript
|
|
177
|
-
var AVCaptureDevice = require('AVFoundation/AVCaptureDevice');
|
|
178
|
-
|
|
179
|
-
// Get back camera
|
|
180
|
-
var devices = AVCaptureDevice.devicesWithMediaType('vide');
|
|
181
|
-
var backCamera = null;
|
|
182
|
-
|
|
183
|
-
for (var i = 0; i < devices.count(); i++) {
|
|
184
|
-
var device = devices.objectAtIndex(i);
|
|
185
|
-
if (device.position() === 1) { // AVCaptureDevicePositionBack
|
|
186
|
-
backCamera = device;
|
|
187
|
-
break;
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// Configure camera
|
|
192
|
-
if (backCamera) {
|
|
193
|
-
backCamera.lockForConfiguration(null);
|
|
194
|
-
backCamera.setFlashMode(1); // AVCaptureFlashModeOn
|
|
195
|
-
backCamera.unlockForConfiguration();
|
|
196
|
-
}
|
|
197
|
-
```
|
|
198
|
-
|
|
199
|
-
#### Access Address Book (contacts)
|
|
200
|
-
|
|
201
|
-
```javascript
|
|
202
|
-
var ABAddressBook = require('AddressBook/ABAddressBook');
|
|
203
|
-
|
|
204
|
-
var addressBook = ABAddressBook.create();
|
|
205
|
-
var people = addressBook.people();
|
|
206
|
-
|
|
207
|
-
for (var i = 0; i < people.count(); i++) {
|
|
208
|
-
var person = people.objectAtIndex(i);
|
|
209
|
-
var firstName = person.valueForProperty(0); // kABPersonFirstNameProperty
|
|
210
|
-
Ti.API.info('Contact: ' + firstName);
|
|
211
|
-
}
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
#### Access CoreLocation (enhanced)
|
|
215
|
-
|
|
216
|
-
```javascript
|
|
217
|
-
var CLLocationManager = require('CoreLocation/CLLocationManager');
|
|
218
|
-
var CLLocation = require('CoreLocation/CLLocation');
|
|
219
|
-
|
|
220
|
-
var locationManager = CLLocationManager.alloc().init();
|
|
221
|
-
locationManager.setDelegate(null);
|
|
222
|
-
locationManager.setDesiredAccuracy(3); // kCLLocationAccuracyBest
|
|
223
|
-
locationManager.startUpdatingLocation();
|
|
224
|
-
```
|
|
225
|
-
|
|
226
|
-
### Android Hyperloop examples
|
|
227
|
-
|
|
228
|
-
#### Access Android frameworks
|
|
229
|
-
|
|
230
|
-
```javascript
|
|
231
|
-
const Context = require('android.content.Context');
|
|
232
|
-
const Activity = require('android.app.Activity');
|
|
233
|
-
const Toast = require('android.widget.Toast');
|
|
234
|
-
|
|
235
|
-
// Get current activity
|
|
236
|
-
const activity = Ti.Android.currentActivity;
|
|
237
|
-
|
|
238
|
-
// Show toast
|
|
239
|
-
Toast.makeText(
|
|
240
|
-
activity,
|
|
241
|
-
'Hello from Hyperloop',
|
|
242
|
-
Toast.LENGTH_SHORT
|
|
243
|
-
).show();
|
|
15
|
+
```sh
|
|
16
|
+
ti create --type module --id com.example.thing --name thing --platforms android --workspace-dir .
|
|
244
17
|
```
|
|
245
18
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
```javascript
|
|
249
|
-
const Vibrator = require('android.os.Vibrator');
|
|
250
|
-
const Context = require('android.content.Context');
|
|
19
|
+
`--platforms` takes `android`, `ios` or both, and each gets its own directory
|
|
20
|
+
with its own build.
|
|
251
21
|
|
|
252
|
-
const activity = Ti.Android.currentActivity;
|
|
253
|
-
const vibrator = activity.getSystemService(Context.VIBRATOR_SERVICE);
|
|
254
|
-
|
|
255
|
-
// Vibrate for 500ms
|
|
256
|
-
vibrator.vibrate(500);
|
|
257
22
|
```
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
// Get phone state
|
|
273
|
-
const state = tm.getCallState();
|
|
274
|
-
if (state === 0) {
|
|
275
|
-
Ti.API.info('Phone idle');
|
|
276
|
-
} else if (state === 1) {
|
|
277
|
-
Ti.API.info('Phone ringing');
|
|
278
|
-
} else if (state === 2) {
|
|
279
|
-
Ti.API.info('Phone offhook');
|
|
280
|
-
}
|
|
23
|
+
thing/
|
|
24
|
+
android/
|
|
25
|
+
manifest id, version, and what it needs
|
|
26
|
+
timodule.xml module-level tiapp settings
|
|
27
|
+
build.gradle
|
|
28
|
+
src/com/example/thing/
|
|
29
|
+
ThingModule.java the module object
|
|
30
|
+
ExampleProxy.java a proxy
|
|
31
|
+
lib/ third-party jars
|
|
32
|
+
platform/ files copied into the app build
|
|
33
|
+
assets/
|
|
34
|
+
documentation/index.md
|
|
35
|
+
example/app.js runs against the built module
|
|
36
|
+
LICENSE
|
|
281
37
|
```
|
|
282
38
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
```javascript
|
|
286
|
-
const WifiManager = require('android.net.wifi.WifiManager');
|
|
287
|
-
const Context = require('android.content.Context');
|
|
288
|
-
|
|
289
|
-
const activity = Ti.Android.currentActivity;
|
|
290
|
-
const wifi = activity.getSystemService(Context.WIFI_SERVICE);
|
|
39
|
+
## The manifest
|
|
291
40
|
|
|
292
|
-
// Check WiFi enabled
|
|
293
|
-
if (wifi.isWifiEnabled()) {
|
|
294
|
-
// Get connection info
|
|
295
|
-
const info = wifi.getConnectionInfo();
|
|
296
|
-
const ssid = info.getSSID();
|
|
297
|
-
const speed = info.getLinkSpeed();
|
|
298
|
-
Ti.API.info(`Connected to: ${ssid} at ${speed} Mbps`);
|
|
299
|
-
}
|
|
300
41
|
```
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
// Get installed apps
|
|
311
|
-
const packages = pm.getInstalledApplications(0);
|
|
312
|
-
for (let i = 0; i < packages.size(); i++) {
|
|
313
|
-
const pkg = packages.get(i);
|
|
314
|
-
const appName = pm.getApplicationLabel(pkg);
|
|
315
|
-
Ti.API.info(`App: ${appName}`);
|
|
316
|
-
}
|
|
42
|
+
version: 1.0.0
|
|
43
|
+
apiversion: 4
|
|
44
|
+
architectures: arm64-v8a armeabi-v7a x86 x86_64
|
|
45
|
+
name: thing
|
|
46
|
+
moduleid: com.example.thing
|
|
47
|
+
guid: 1bf6ca03-c384-44f1-8bdc-f06454872771
|
|
48
|
+
platform: android
|
|
49
|
+
minsdk: 13.4.1
|
|
317
50
|
```
|
|
318
51
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
2. Handle errors. Native calls can throw exceptions.
|
|
323
|
-
3. Memory management: be aware of native object lifecycles.
|
|
324
|
-
4. Test thoroughly. Native APIs have platform-specific behaviors.
|
|
325
|
-
5. Document requirements. Note platform versions and permissions needed.
|
|
326
|
-
6. Use try/catch. Wrap native calls for safety.
|
|
327
|
-
|
|
328
|
-
```javascript
|
|
329
|
-
try {
|
|
330
|
-
const NativeClass = require('path.to.NativeClass');
|
|
331
|
-
const instance = new NativeClass();
|
|
332
|
-
// Use instance
|
|
333
|
-
} catch (e) {
|
|
334
|
-
Ti.API.error(`Hyperloop error: ${e.message}`);
|
|
335
|
-
}
|
|
336
|
-
```
|
|
52
|
+
`minsdk` is the oldest Titanium SDK the module supports, and the build refuses
|
|
53
|
+
when a project's SDK is older. `apiversion` is the module API generation - 4 on
|
|
54
|
+
Android, 2 on iOS for 13.4.
|
|
337
55
|
|
|
338
|
-
|
|
56
|
+
Bump `version` on every release. It is what a project pins against.
|
|
339
57
|
|
|
340
|
-
|
|
58
|
+
## What the code looks like
|
|
341
59
|
|
|
342
|
-
|
|
343
|
-
- Reuse native code across multiple Titanium apps
|
|
344
|
-
- Distribute functionality to other developers
|
|
345
|
-
- Implement complex native functionality not suitable for Hyperloop
|
|
346
|
-
- Maintain a stable API regardless of native SDK changes
|
|
347
|
-
|
|
348
|
-
### Android module development
|
|
349
|
-
|
|
350
|
-
#### Quick start
|
|
351
|
-
|
|
352
|
-
1. Create module structure:
|
|
353
|
-
```
|
|
354
|
-
com.example.mymodule/
|
|
355
|
-
├── src/
|
|
356
|
-
│ └── com/
|
|
357
|
-
│ └── example/
|
|
358
|
-
│ └── mymodule/
|
|
359
|
-
│ ├── ExampleModule.java
|
|
360
|
-
│ └── ExampleProxy.java
|
|
361
|
-
├── build.properties
|
|
362
|
-
├── build.xml
|
|
363
|
-
├── timodule.xml
|
|
364
|
-
└── LICENSE
|
|
365
|
-
```
|
|
366
|
-
|
|
367
|
-
2. Module Java class:
|
|
60
|
+
Titanium's Android bridge is Kroll, driven by annotations:
|
|
368
61
|
|
|
369
62
|
```java
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
import org.appcelerator.kroll.KrollModule;
|
|
373
|
-
import org.appcelerator.kroll.annotations.KrollModule;
|
|
374
|
-
|
|
375
|
-
@KrollModule(name="MyModule", id="com.example.mymodule")
|
|
376
|
-
public class ExampleModule extends KrollModule {
|
|
377
|
-
|
|
378
|
-
public ExampleModule() {
|
|
379
|
-
super();
|
|
380
|
-
}
|
|
63
|
+
@Kroll.module(name = "Thing", id = "com.example.thing")
|
|
64
|
+
public class ThingModule extends KrollModule {
|
|
381
65
|
|
|
382
66
|
@Kroll.method
|
|
383
|
-
public String
|
|
384
|
-
return "Hello
|
|
67
|
+
public String greet(String name) {
|
|
68
|
+
return "Hello " + name;
|
|
385
69
|
}
|
|
386
70
|
|
|
387
|
-
@Kroll.
|
|
388
|
-
public
|
|
389
|
-
return a + b;
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
@Kroll.getProperty
|
|
393
|
-
public String getVersion() {
|
|
394
|
-
return "1.0.0";
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
```
|
|
398
|
-
|
|
399
|
-
3. `timodule.xml`:
|
|
400
|
-
```xml
|
|
401
|
-
<module>
|
|
402
|
-
<description>My Example Module</description>
|
|
403
|
-
<version>1.0.0</version>
|
|
404
|
-
<platform>android</platform>
|
|
405
|
-
<min-sdk>3.0.0.GA</min-sdk>
|
|
406
|
-
</module>
|
|
407
|
-
```
|
|
408
|
-
|
|
409
|
-
4. Build and package:
|
|
410
|
-
```bash
|
|
411
|
-
ant clean
|
|
412
|
-
ant package
|
|
413
|
-
```
|
|
414
|
-
|
|
415
|
-
#### Use in Titanium app
|
|
416
|
-
|
|
417
|
-
```javascript
|
|
418
|
-
const myModule = require('com.example.mymodule');
|
|
419
|
-
Ti.API.info(myModule.helloWorld()); // "Hello from native module!"
|
|
420
|
-
Ti.API.info(myModule.add(5, 3)); // 8
|
|
421
|
-
Ti.API.info(myModule.version); // "1.0.0"
|
|
422
|
-
```
|
|
423
|
-
|
|
424
|
-
#### Common Android module patterns
|
|
425
|
-
|
|
426
|
-
Accessing Android APIs:
|
|
427
|
-
|
|
428
|
-
```java
|
|
429
|
-
import android.content.Context;
|
|
430
|
-
import android.location.LocationManager;
|
|
431
|
-
|
|
432
|
-
@Kroll.method
|
|
433
|
-
public boolean isGPSEnabled() {
|
|
434
|
-
LocationManager lm = (LocationManager) getActivity().getSystemService(Context.LOCATION_SERVICE);
|
|
435
|
-
return lm.isProviderEnabled(LocationManager.GPS_PROVIDER);
|
|
71
|
+
@Kroll.constant
|
|
72
|
+
public static final String VERSION = "1.0.0";
|
|
436
73
|
}
|
|
437
74
|
```
|
|
438
75
|
|
|
439
|
-
|
|
76
|
+
`@Kroll.method` exposes a method, `@Kroll.getProperty` and
|
|
77
|
+
`@Kroll.setProperty` make a property, and `@Kroll.constant` makes a constant.
|
|
78
|
+
A `KrollProxy` subclass becomes an object your JavaScript can create.
|
|
440
79
|
|
|
441
|
-
|
|
442
|
-
|
|
80
|
+
On iOS the equivalent is an Objective-C class extending `TiModule`, where
|
|
81
|
+
methods are exposed by naming convention rather than annotation.
|
|
443
82
|
|
|
444
|
-
|
|
445
|
-
public void triggerEvent() {
|
|
446
|
-
KrollDict data = new KrollDict();
|
|
447
|
-
data.put("message", "Hello from native!");
|
|
448
|
-
fireEvent("myEvent", data);
|
|
449
|
-
}
|
|
450
|
-
```
|
|
83
|
+
## Build and test
|
|
451
84
|
|
|
452
|
-
|
|
85
|
+
Build from the module directory:
|
|
453
86
|
|
|
454
|
-
```
|
|
455
|
-
|
|
456
|
-
public void startService() {
|
|
457
|
-
// Start service and listen for events
|
|
458
|
-
}
|
|
87
|
+
```sh
|
|
88
|
+
ti build --platform android --build-only
|
|
459
89
|
```
|
|
460
90
|
|
|
461
|
-
|
|
91
|
+
That produces a distributable zip. Use `example/app.js` in the module test workflow; the `--build-only` packaging command does not itself demonstrate runtime behavior. It is an example app you run against the built module, which is the fastest way to exercise what you just wrote.
|
|
462
92
|
|
|
463
|
-
|
|
93
|
+
Install it into a project by unzipping it there - see
|
|
94
|
+
[using modules](https://titaniumsdk.com/docs/build/modules.md) for the layout and for declaring it in
|
|
95
|
+
`tiapp.xml`.
|
|
464
96
|
|
|
465
|
-
|
|
466
|
-
```bash
|
|
467
|
-
# Use Titanium CLI
|
|
468
|
-
titanium create --type module --id com.example.mymodule --name MyModule --platform ios
|
|
469
|
-
```
|
|
97
|
+
## Things that catch people
|
|
470
98
|
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
├── Classes/
|
|
475
|
-
│ ├── ComExampleMymoduleModule.h
|
|
476
|
-
│ ├── ComExampleMymoduleModule.m
|
|
477
|
-
│ └── ComExampleMymoduleProxy.h
|
|
478
|
-
├── documentation/
|
|
479
|
-
│ └── index.md
|
|
480
|
-
├── assets/
|
|
481
|
-
├── iphone/
|
|
482
|
-
│ └── MyModule_Prefix.pch
|
|
483
|
-
├── timodule.xml
|
|
484
|
-
└── LICENSE
|
|
485
|
-
```
|
|
99
|
+
**`apiversion` and `minsdk` are promises.** A module built against an old SDK
|
|
100
|
+
may link against internals that moved. When a project fails at link time with a
|
|
101
|
+
message naming nothing, a stale module is the first suspect.
|
|
486
102
|
|
|
487
|
-
|
|
103
|
+
**Architectures cost size.** Every one in `architectures` is compiled and
|
|
104
|
+
shipped. Drop the ones you do not need.
|
|
488
105
|
|
|
489
|
-
|
|
490
|
-
|
|
106
|
+
**The bridge is the slow part.** A call from JavaScript into native crosses
|
|
107
|
+
Kroll, and a loop that crosses per iteration is where the time goes. Pass a
|
|
108
|
+
collection and return a collection rather than calling per item.
|
|
491
109
|
|
|
492
|
-
|
|
110
|
+
**Threading is yours to get right.** A long native call blocks the JavaScript
|
|
111
|
+
thread. Do the work on a background thread and call back.
|
|
493
112
|
|
|
494
|
-
|
|
113
|
+
## Next
|
|
495
114
|
|
|
496
|
-
|
|
497
|
-
return @"com.example.mymodule";
|
|
498
|
-
}
|
|
115
|
+
[Hyperloop](https://titaniumsdk.com/docs/extend/hyperloop.md) does much of this without a module.
|
|
499
116
|
|
|
500
|
-
- (NSString *)moduleName {
|
|
501
|
-
return @"MyModule";
|
|
502
|
-
}
|
|
503
117
|
|
|
504
|
-
|
|
118
|
+
## Hyperloop
|
|
505
119
|
|
|
506
|
-
|
|
507
|
-
[super startup];
|
|
508
|
-
DebugLog(@"[DEBUG] MyModule initialized");
|
|
509
|
-
}
|
|
120
|
+
Based on the [current official guide](https://titaniumsdk.com/docs/extend/hyperloop.md). Examples target released SDK 13.4.1; verify the selected SDK before adopting version-specific behavior.
|
|
510
121
|
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
}
|
|
122
|
+
Hyperloop lets JavaScript call native classes directly. Instead of writing a
|
|
123
|
+
module to wrap `UIImpactFeedbackGenerator`, you require it and use it.
|
|
514
124
|
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
- (void)dealloc {
|
|
518
|
-
[super dealloc];
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
#pragma Public APIs
|
|
522
|
-
|
|
523
|
-
- (NSString*)version {
|
|
524
|
-
return @"1.0.0";
|
|
525
|
-
}
|
|
125
|
+
```js
|
|
126
|
+
const UIImpactFeedbackGenerator = require('UIKit/UIImpactFeedbackGenerator');
|
|
526
127
|
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
- (NSNumber*)add:(NSNumber*)a with:(NSNumber*)b {
|
|
532
|
-
return [NSNumber numberWithInt:[a intValue] + [b intValue]];
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
@end
|
|
536
|
-
```
|
|
537
|
-
|
|
538
|
-
4. `timodule.xml`:
|
|
539
|
-
```xml
|
|
540
|
-
<module>
|
|
541
|
-
<description>My Example Module</description>
|
|
542
|
-
<version>1.0.0</version>
|
|
543
|
-
<platform>iphone</platform>
|
|
544
|
-
<min-sdk>3.0.0.GA</min-sdk>
|
|
545
|
-
</module>
|
|
546
|
-
```
|
|
547
|
-
|
|
548
|
-
5. Build and package:
|
|
549
|
-
```bash
|
|
550
|
-
./build.py
|
|
128
|
+
const generator = UIImpactFeedbackGenerator.alloc().init();
|
|
129
|
+
generator.impactOccurred();
|
|
551
130
|
```
|
|
552
131
|
|
|
553
|
-
|
|
132
|
+
```js
|
|
133
|
+
const Toast = require('android.widget.Toast');
|
|
134
|
+
const Activity = Ti.Android.currentActivity;
|
|
554
135
|
|
|
555
|
-
|
|
556
|
-
const myModule = require('com.example.mymodule');
|
|
557
|
-
Ti.API.info(myModule.helloWorld()); // "Hello from native module!"
|
|
558
|
-
Ti.API.info(myModule.add(5, { with: 3 })); // 8
|
|
559
|
-
Ti.API.info(myModule.version); // "1.0.0"
|
|
136
|
+
Toast.makeText(Activity, 'Saved', Toast.LENGTH_SHORT).show();
|
|
560
137
|
```
|
|
561
138
|
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
Accessing iOS frameworks:
|
|
139
|
+
It is a module, maintained by TiDev, and still current: 8.0.1 is the release
|
|
140
|
+
against SDK 13.4.
|
|
565
141
|
|
|
566
|
-
|
|
567
|
-
#import <CoreLocation/CoreLocation.h>
|
|
142
|
+
## Install
|
|
568
143
|
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
144
|
+
```xml
|
|
145
|
+
<modules>
|
|
146
|
+
<module platform="android">hyperloop</module>
|
|
147
|
+
<module platform="iphone">hyperloop</module>
|
|
148
|
+
</modules>
|
|
574
149
|
```
|
|
575
150
|
|
|
576
|
-
|
|
151
|
+
The module is unpacked like any other - see
|
|
152
|
+
[using modules](https://titaniumsdk.com/docs/build/modules.md).
|
|
577
153
|
|
|
578
|
-
|
|
579
|
-
- (void)locationUpdated:(CLLocation*)location {
|
|
580
|
-
NSDictionary *event = @{
|
|
581
|
-
@"latitude": [NSNumber numberWithDouble:location.coordinate.latitude],
|
|
582
|
-
@"longitude": [NSNumber numberWithDouble:location.coordinate.longitude]
|
|
583
|
-
};
|
|
584
|
-
[self fireEvent:@"locationUpdate" withObject:event];
|
|
585
|
-
}
|
|
586
|
-
```
|
|
154
|
+
## How the require works
|
|
587
155
|
|
|
588
|
-
|
|
156
|
+
The path is the native type's own name.
|
|
589
157
|
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
158
|
+
| Platform | Form | Example |
|
|
159
|
+
| -------- | --------------------- | --------------------------------- |
|
|
160
|
+
| iOS | `Framework/Class` | `require('UIKit/UIView')` |
|
|
161
|
+
| Android | Fully-qualified class | `require('android.widget.Toast')` |
|
|
594
162
|
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
}
|
|
599
|
-
return myView;
|
|
600
|
-
}
|
|
163
|
+
Hyperloop generates a JavaScript wrapper for each type you require, at build
|
|
164
|
+
time, by reading the platform's own metadata. That is why a type you have not
|
|
165
|
+
required costs nothing.
|
|
601
166
|
|
|
602
|
-
|
|
603
|
-
```
|
|
167
|
+
## Hyperloop or a module
|
|
604
168
|
|
|
605
|
-
|
|
169
|
+
**Hyperloop** for a handful of calls, for something a single app needs, and for
|
|
170
|
+
trying an API out. No native toolchain knowledge and no separate build.
|
|
606
171
|
|
|
607
|
-
|
|
172
|
+
**A module** for something several apps need, for anything with real native
|
|
173
|
+
logic behind it, and when you want to ship a versioned artifact. See
|
|
174
|
+
[native modules](https://titaniumsdk.com/docs/extend/modules.md).
|
|
608
175
|
|
|
609
|
-
|
|
176
|
+
The wrong choice in one direction is a module for three lines. In the other, it
|
|
177
|
+
is the same Hyperloop code pasted into four apps.
|
|
610
178
|
|
|
611
|
-
|
|
612
|
-
mymodule/
|
|
613
|
-
└── ios/
|
|
614
|
-
├── manifest
|
|
615
|
-
├── spm.json
|
|
616
|
-
├── MyModule.xcodeproj
|
|
617
|
-
└── Classes/
|
|
618
|
-
```
|
|
179
|
+
## What it costs
|
|
619
180
|
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
"dependencies": [
|
|
623
|
-
{
|
|
624
|
-
"remotePackageReference": "Parse-SDK-iOS-OSX",
|
|
625
|
-
"repositoryURL": "https://github.com/parse-community/Parse-SDK-iOS-OSX",
|
|
626
|
-
"requirementKind": "upToNextMajorVersion",
|
|
627
|
-
"requirementMinimumVersion": "5.1.1",
|
|
628
|
-
"products": [
|
|
629
|
-
{ "productName": "ParseObjC", "frameworkName": "ParseObjC", "linkage": "host" },
|
|
630
|
-
{ "productName": "ParseLiveQuery", "frameworkName": "ParseLiveQuery", "linkage": "embedded" }
|
|
631
|
-
]
|
|
632
|
-
}
|
|
633
|
-
]
|
|
634
|
-
}
|
|
635
|
-
```
|
|
181
|
+
**Build time.** Hyperloop scans your JavaScript for requires and generates
|
|
182
|
+
wrappers for each. A build with many is measurably slower.
|
|
636
183
|
|
|
637
|
-
|
|
184
|
+
**Every call crosses the bridge.** Native code called in a loop from JavaScript
|
|
185
|
+
pays per iteration. Keep native work on the native side.
|
|
638
186
|
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
| `remotePackageReference` | no | last path segment of `repositoryURL` | Reference name in the generated Xcode project. |
|
|
643
|
-
| `requirementKind` | no | `upToNextMajorVersion` | Version resolution rule, e.g. `upToNextMajorVersion`, `upToNextMinorVersion`. |
|
|
644
|
-
| `requirementMinimumVersion` | no | `1.0.0` | Minimum package version to resolve. |
|
|
645
|
-
| `linkage` | no | `embedded` | Default linkage for every product of this dependency. |
|
|
646
|
-
| `products` | yes | — | Non-empty array of products to link. A dependency with no valid product is skipped with a warning. |
|
|
187
|
+
**You are using the platform API directly**, so there is no cross-platform
|
|
188
|
+
shape and no Titanium safety net. Guard with `OS_IOS` and `OS_ANDROID`, or
|
|
189
|
+
`Ti.Platform.osname`.
|
|
647
190
|
|
|
648
|
-
|
|
191
|
+
**The API is the platform's**, so its documentation is Apple's and Google's.
|
|
192
|
+
Titanium's reference does not describe it.
|
|
649
193
|
|
|
650
|
-
|
|
651
|
-
|---|---|---|---|
|
|
652
|
-
| `productName` | yes | — | Product name as the Swift package declares it. |
|
|
653
|
-
| `frameworkName` | no | `productName` | Framework name in the Frameworks build phase, when it differs. |
|
|
654
|
-
| `linkage` | no | the dependency's `linkage` | Per-product override: `embedded` or `host`. |
|
|
194
|
+
## Next
|
|
655
195
|
|
|
656
|
-
|
|
196
|
+
[CLI plugins](https://titaniumsdk.com/docs/extend/cli-plugins.md) is the other extension point: changing
|
|
197
|
+
the build rather than the app.
|
|
657
198
|
|
|
658
|
-
`embedded` bakes the product into the module binary. The module stays self-contained and the app's Xcode project is untouched. Use it for small, module-private dependencies.
|
|
659
199
|
|
|
660
|
-
|
|
200
|
+
## CLI plugins
|
|
661
201
|
|
|
662
|
-
|
|
202
|
+
Based on the [current official guide](https://titaniumsdk.com/docs/extend/cli-plugins.md). Examples target released SDK 13.4.1; verify the selected SDK before adopting version-specific behavior.
|
|
663
203
|
|
|
664
|
-
|
|
204
|
+
A CLI plugin is JavaScript the CLI loads and lets subscribe to build events. It
|
|
205
|
+
changes the build, not the app: generating a file before compilation, injecting
|
|
206
|
+
a value, uploading an artifact afterwards.
|
|
665
207
|
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
ti
|
|
669
|
-
|
|
208
|
+
Alloy is one. `plugins/ti.alloy/hooks/alloy.js` in an Alloy project is a hook
|
|
209
|
+
that compiles `app/` into `Resources/` before every build - which is why an
|
|
210
|
+
Alloy project needs `ti.alloy` in `tiapp.xml` and is otherwise a Classic
|
|
211
|
+
project.
|
|
670
212
|
|
|
671
|
-
The
|
|
213
|
+
## The shape of one
|
|
672
214
|
|
|
673
|
-
```
|
|
674
|
-
|
|
675
|
-
|
|
215
|
+
```js
|
|
216
|
+
// plugins/ti.example/hooks/stamp.js
|
|
217
|
+
exports.cliVersion = '>=3.X';
|
|
218
|
+
exports.version = '1.0.0';
|
|
676
219
|
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
220
|
+
exports.init = (logger, config, cli, appc) => {
|
|
221
|
+
cli.on('build.pre.compile', {
|
|
222
|
+
priority: 1000,
|
|
223
|
+
post(builder, done) {
|
|
224
|
+
logger.info(`building ${builder.tiapp.name} ${builder.tiapp.version}`);
|
|
225
|
+
done();
|
|
226
|
+
},
|
|
227
|
+
});
|
|
228
|
+
};
|
|
229
|
+
```
|
|
684
230
|
|
|
685
|
-
|
|
231
|
+
`init` is called once with the logger, the CLI config, the CLI itself, and the
|
|
232
|
+
`node-appc` helpers. `cliVersion` is a range the plugin needs, and a plugin
|
|
233
|
+
whose range does not match is skipped rather than crashing the build.
|
|
686
234
|
|
|
687
|
-
|
|
688
|
-
# For iOS
|
|
689
|
-
./build.py -d
|
|
235
|
+
A hook subscribes with `pre` or `post`. In the two-argument callback form shown above, **call `done()`** when finished or the build waits. The current CLI also awaits a one-argument async handler; do not mix callback completion with an unfinished asynchronous task.
|
|
690
236
|
|
|
691
|
-
|
|
692
|
-
ant dist
|
|
693
|
-
```
|
|
237
|
+
`priority` orders hooks against each other; the default is 1000.
|
|
694
238
|
|
|
695
|
-
|
|
239
|
+
## Where they are found
|
|
696
240
|
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
241
|
+
| Location | Scope |
|
|
242
|
+
| ------------------------------- | ------------ |
|
|
243
|
+
| `<project>/plugins/<id>/hooks/` | That project |
|
|
244
|
+
| `paths.hooks` in CLI config | That machine |
|
|
245
|
+
| The SDK's own `cli/hooks/` | Everything |
|
|
700
246
|
|
|
701
|
-
|
|
247
|
+
Declare a project plugin in `tiapp.xml`:
|
|
702
248
|
|
|
703
249
|
```xml
|
|
704
|
-
<
|
|
705
|
-
<
|
|
706
|
-
|
|
707
|
-
</modules>
|
|
250
|
+
<plugins>
|
|
251
|
+
<plugin version="1.0">ti.example</plugin>
|
|
252
|
+
</plugins>
|
|
708
253
|
```
|
|
709
254
|
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
### Use Hyperloop when
|
|
255
|
+
Add a machine-wide path with
|
|
256
|
+
[CLI configuration](https://titaniumsdk.com/docs/reference/config.md):
|
|
713
257
|
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
- Quick integration with platform features
|
|
718
|
-
- You do not need to distribute code
|
|
258
|
+
```sh
|
|
259
|
+
ti config paths.hooks --append ~/titanium-hooks
|
|
260
|
+
```
|
|
719
261
|
|
|
720
|
-
|
|
262
|
+
A machine-wide hook changes builds for every project on that machine and for
|
|
263
|
+
nobody else, so a build stops being reproducible from the repository alone. Use
|
|
264
|
+
the project location unless you specifically want the opposite.
|
|
721
265
|
|
|
722
|
-
|
|
723
|
-
- Distributing to other developers
|
|
724
|
-
- Complex native logic with state management
|
|
725
|
-
- Need stable API abstraction
|
|
726
|
-
- Performance-critical code paths
|
|
727
|
-
- Custom UI components
|
|
266
|
+
## Useful events
|
|
728
267
|
|
|
729
|
-
|
|
268
|
+
| Event | When |
|
|
269
|
+
| --------------------- | --------------------------------------------- |
|
|
270
|
+
| `build.pre.construct` | Before the builder is set up |
|
|
271
|
+
| `build.pre.compile` | Before compilation. Where generated code goes |
|
|
272
|
+
| `build.post.compile` | After compilation, before packaging |
|
|
273
|
+
| `build.finalize` | The build is done |
|
|
274
|
+
| `cli:pre-execute` | Before any command runs |
|
|
275
|
+
| `cli:post-execute` | After it finishes |
|
|
730
276
|
|
|
731
|
-
|
|
277
|
+
The build events carry the `builder`, which is where `tiapp`, the project
|
|
278
|
+
directory and the resolved options are. Read it rather than re-parsing
|
|
279
|
+
`tiapp.xml`.
|
|
732
280
|
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
- https://github.com/search?q=titanium+module
|
|
281
|
+
Platform builders emit their own more specific events too. `--debug` prints the
|
|
282
|
+
events as they fire, which is the practical way to find the one you want.
|
|
736
283
|
|
|
737
|
-
|
|
284
|
+
## Alloy's own hook file
|
|
738
285
|
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
- ti.admob - AdMob integration
|
|
743
|
-
- ti.facebook - Facebook SDK
|
|
744
|
-
- ti.googleplus - Google+ SDK
|
|
745
|
-
- ti.oauth2 - OAuth 2.0 client
|
|
286
|
+
For work tied to Alloy's compile rather than the CLI's build, `app/alloy.jmk`
|
|
287
|
+
is simpler and does not need a plugin. See
|
|
288
|
+
[Alloy configuration](https://titaniumsdk.com/docs/alloy/config.md).
|
|
746
289
|
|
|
747
|
-
|
|
290
|
+
## Keep them cheap
|
|
748
291
|
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
292
|
+
A hook runs on every build, including the rebuild you do twenty times an hour.
|
|
293
|
+
Anything slow belongs behind a check that skips it when there is nothing to do,
|
|
294
|
+
or in a `production`-only branch.
|
|
752
295
|
|
|
753
|
-
|
|
754
|
-
<modules>
|
|
755
|
-
<module platform="android">com.mapbox.map</module>
|
|
756
|
-
<module platform="iphone">com.mapbox.map</module>
|
|
757
|
-
</modules>
|
|
758
|
-
```
|
|
759
|
-
|
|
760
|
-
4. Require in code:
|
|
761
|
-
|
|
762
|
-
```javascript
|
|
763
|
-
const Mapbox = require('com.mapbox.map');
|
|
764
|
-
```
|
|
296
|
+
## Next
|
|
765
297
|
|
|
766
|
-
|
|
298
|
+
Back to [building apps](https://titaniumsdk.com/docs/build.md), or the
|
|
299
|
+
[CLI reference](https://titaniumsdk.com/docs/reference/cli.md) for the commands these hook into.
|
|
767
300
|
|
|
768
|
-
1.
|
|
769
|
-
2. Prefer Hyperloop for simple cases; less overhead.
|
|
770
|
-
3. Create modules for reusable code; better distribution.
|
|
771
|
-
4. Handle platform differences; branch code appropriately.
|
|
772
|
-
5. Document dependencies; note SDK versions and requirements.
|
|
773
|
-
6. Test on real devices; simulators may not support all features.
|
|
774
|
-
7. Version your modules; use semantic versioning for compatibility.
|
|
775
|
-
8. Provide examples; help users understand usage patterns.
|
|
301
|
+
CLI contract verification: Titanium CLI 9.1.0 `src/cli.js` (`createHook`, `emit`, `scanHooks`) and SDK 13.4.1 `build.pre.compile` emission. An event hook receives the supplied builder payload augmented with hook metadata; other events may have different payloads.
|