@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.
Files changed (98) hide show
  1. package/CONTRIBUTING.md +1 -1
  2. package/README.md +31 -60
  3. package/agents/ti-pro.md +3 -1
  4. package/commands/ti-audit.md +6 -4
  5. package/docs/examples.md +9 -0
  6. package/docs/knowledge-index.md +2 -1
  7. package/lib/config.js +1 -0
  8. package/package.json +1 -1
  9. package/skills/alloy-guides/SKILL.md +18 -179
  10. package/skills/alloy-guides/references/CLI_TASKS.md +17 -216
  11. package/skills/alloy-guides/references/CONCEPTS.md +27 -162
  12. package/skills/alloy-guides/references/CONTROLLERS.md +38 -236
  13. package/skills/alloy-guides/references/MODELS.md +47 -723
  14. package/skills/alloy-guides/references/MODELS_ADVANCED.md +21 -482
  15. package/skills/alloy-guides/references/VIEWS_DYNAMIC.md +12 -222
  16. package/skills/alloy-guides/references/VIEWS_STYLES.md +39 -358
  17. package/skills/alloy-guides/references/VIEWS_XML.md +36 -552
  18. package/skills/alloy-guides/references/WIDGETS.md +49 -127
  19. package/skills/alloy-howtos/SKILL.md +14 -169
  20. package/skills/alloy-howtos/references/best_practices.md +9 -113
  21. package/skills/alloy-howtos/references/cli_reference.md +18 -223
  22. package/skills/alloy-howtos/references/config_files.md +50 -133
  23. package/skills/alloy-howtos/references/debugging_troubleshooting.md +19 -63
  24. package/skills/alloy-howtos/references/samples.md +21 -138
  25. package/skills/ti-api/SKILL.md +10 -2
  26. package/skills/ti-api/references/api-android.md +36 -2
  27. package/skills/ti-api/references/api-app-platform.md +39 -3
  28. package/skills/ti-api/references/api-core.md +50 -18
  29. package/skills/ti-api/references/api-data-network.md +49 -35
  30. package/skills/ti-api/references/api-media.md +45 -27
  31. package/skills/ti-api/references/api-services.md +39 -9
  32. package/skills/ti-api/references/api-ui-android.md +33 -13
  33. package/skills/ti-api/references/api-ui-extras.md +41 -15
  34. package/skills/ti-api/references/api-ui-ios-animator.md +42 -26
  35. package/skills/ti-api/references/api-ui-ios.md +74 -2
  36. package/skills/ti-api/references/api-ui-lists.md +32 -16
  37. package/skills/ti-api/references/api-ui-text-input.md +34 -17
  38. package/skills/ti-api/references/api-ui-views.md +27 -1
  39. package/skills/ti-api/references/api-ui-windows-navigation.md +32 -18
  40. package/skills/ti-api/references/api-xml-global.md +66 -16
  41. package/skills/ti-create-release/SKILL.md +98 -0
  42. package/skills/ti-create-release/references/module-discovery.md +69 -0
  43. package/skills/ti-expert/references/adaptive-layouts.md +1 -1
  44. package/skills/ti-guides/SKILL.md +37 -76
  45. package/skills/ti-guides/references/advanced-data-and-images.md +122 -126
  46. package/skills/ti-guides/references/android-manifest.md +24 -83
  47. package/skills/ti-guides/references/app-distribution.md +31 -377
  48. package/skills/ti-guides/references/application-frameworks.md +127 -335
  49. package/skills/ti-guides/references/cli-reference.md +184 -633
  50. package/skills/ti-guides/references/commonjs-advanced.md +81 -231
  51. package/skills/ti-guides/references/compatibility-matrix.md +36 -75
  52. package/skills/ti-guides/references/hello-world.md +95 -70
  53. package/skills/ti-guides/references/hyperloop-native-access.md +52 -448
  54. package/skills/ti-guides/references/resources.md +174 -142
  55. package/skills/ti-guides/references/sdk-release-notes.md +28 -71
  56. package/skills/ti-guides/references/tiapp-config.md +222 -598
  57. package/skills/ti-howtos/SKILL.md +23 -123
  58. package/skills/ti-howtos/references/cross-platform-development.md +119 -324
  59. package/skills/ti-howtos/references/debugging-profiling.md +77 -445
  60. package/skills/ti-howtos/references/extending-titanium.md +199 -673
  61. package/skills/ti-howtos/references/local-data-sources.md +87 -300
  62. package/skills/ti-howtos/references/location-and-maps.md +86 -213
  63. package/skills/ti-howtos/references/media-apis.md +103 -223
  64. package/skills/ti-howtos/references/notification-services.md +86 -506
  65. package/skills/ti-howtos/references/remote-data-sources.md +97 -316
  66. package/skills/ti-howtos/references/using-modules.md +77 -133
  67. package/skills/ti-ui/SKILL.md +28 -143
  68. package/skills/ti-ui/references/icons-and-splash-screens.md +74 -403
  69. package/skills/ti-ui/references/layouts-and-positioning.md +175 -402
  70. package/skills/ti-ui/references/listviews-and-performance.md +100 -703
  71. package/skills/ti-ui/references/platform-conventions.md +180 -0
  72. package/skills/ti-ui/references/user-interface.md +43 -0
  73. package/skills/alloy-guides/references/VIEWS_WITHOUT_CONTROLLERS.md +0 -102
  74. package/skills/alloy-howtos/references/custom_tags.md +0 -148
  75. package/skills/ti-guides/references/coding-best-practices.md +0 -149
  76. package/skills/ti-guides/references/javascript-primer.md +0 -414
  77. package/skills/ti-guides/references/reserved-words.md +0 -36
  78. package/skills/ti-guides/references/style-and-conventions.md +0 -103
  79. package/skills/ti-howtos/references/android-platform-deep-dives.md +0 -624
  80. package/skills/ti-howtos/references/automation-fastlane-appium.md +0 -96
  81. package/skills/ti-howtos/references/buffer-codec-streams.md +0 -162
  82. package/skills/ti-howtos/references/google-maps-v2.md +0 -172
  83. package/skills/ti-howtos/references/ios-map-kit.md +0 -149
  84. package/skills/ti-howtos/references/ios-platform-deep-dives.md +0 -611
  85. package/skills/ti-howtos/references/tutorials.md +0 -565
  86. package/skills/ti-howtos/references/web-content-integration.md +0 -287
  87. package/skills/ti-howtos/references/webpack-build-pipeline.md +0 -124
  88. package/skills/ti-ui/references/accessibility-deep-dive.md +0 -288
  89. package/skills/ti-ui/references/animation-and-matrices.md +0 -569
  90. package/skills/ti-ui/references/application-structures.md +0 -626
  91. package/skills/ti-ui/references/custom-fonts-styling.md +0 -549
  92. package/skills/ti-ui/references/event-handling.md +0 -401
  93. package/skills/ti-ui/references/gestures.md +0 -448
  94. package/skills/ti-ui/references/orientation.md +0 -374
  95. package/skills/ti-ui/references/platform-ui-android.md +0 -727
  96. package/skills/ti-ui/references/platform-ui-ios.md +0 -576
  97. package/skills/ti-ui/references/scrolling-views.md +0 -288
  98. package/skills/ti-ui/references/tableviews.md +0 -542
@@ -1,775 +1,301 @@
1
- # Extending Titanium
1
+ # Native modules
2
2
 
3
- <!-- TOC-START -->
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
- - [1. Module architecture (core concepts)](#1-module-architecture-core-concepts)
7
- - [2. Module debugging in Xcode (iOS)](#2-module-debugging-in-xcode-ios)
8
- - [3. Upgrading to SDK 9.0.0+ (Android)](#3-upgrading-to-sdk-900-android)
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
- <!-- TOC-END -->
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
- ## 1. Module architecture (core concepts)
13
+ ## Create one
19
14
 
20
- Titanium modules are based on a native class hierarchy that communicates with JavaScript:
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
- #### Access vibrator
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
- #### Access TelephonyManager
260
-
261
- ```javascript
262
- const TelephonyManager = require('android.telephony.TelephonyManager');
263
- const Context = require('android.content.Context');
264
-
265
- const activity = Ti.Android.currentActivity;
266
- const tm = activity.getSystemService(Context.TELEPHONY_SERVICE);
267
-
268
- // Get device ID
269
- const deviceId = tm.getDeviceId();
270
- Ti.API.info(`Device ID: ${deviceId}`);
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
- #### Access WiFi Manager
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
- #### Access PackageManager
303
-
304
- ```javascript
305
- const PackageManager = require('android.content.pm.PackageManager');
306
-
307
- const activity = Ti.Android.currentActivity;
308
- const pm = activity.getPackageManager();
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
- ### Hyperloop best practices
320
-
321
- 1. Check platform availability. Use `Ti.Platform.osname` to branch code.
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
- ## 3. Native module development
56
+ Bump `version` on every release. It is what a project pins against.
339
57
 
340
- ### When to create native modules
58
+ ## What the code looks like
341
59
 
342
- Create a native module when you need to:
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
- package com.example.mymodule;
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 helloWorld() {
384
- return "Hello from native module!";
67
+ public String greet(String name) {
68
+ return "Hello " + name;
385
69
  }
386
70
 
387
- @Kroll.method
388
- public int add(int a, int b) {
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
- Firing events to JavaScript:
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
- ```java
442
- import org.appcelerator.kroll.KrollDict;
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
- @Kroll.method
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
- Receiving events from JavaScript:
85
+ Build from the module directory:
453
86
 
454
- ```java
455
- @Kroll.method
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
- ### iOS module development
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
- #### Quick start
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
- 1. Create module with template:
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
- 2. Module structure:
472
- ```
473
- com.example.mymodule/
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
- 3. Module implementation (`ComExampleMymoduleModule.m`):
103
+ **Architectures cost size.** Every one in `architectures` is compiled and
104
+ shipped. Drop the ones you do not need.
488
105
 
489
- ```objc
490
- #import "ComExampleMymoduleModule.h"
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
- @implementation ComExampleMymoduleModule
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
- #pragma mark Internal
113
+ ## Next
495
114
 
496
- - (id)moduleGUID {
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
- #pragma mark Lifecycle
118
+ ## Hyperloop
505
119
 
506
- - (void)startup {
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
- - (void)shutdown:(id)sender {
512
- [super shutdown:sender];
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
- #pragma mark Cleanup
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
- - (NSString*)helloWorld {
528
- return @"Hello from native module!";
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
- 6. Use in Titanium app:
132
+ ```js
133
+ const Toast = require('android.widget.Toast');
134
+ const Activity = Ti.Android.currentActivity;
554
135
 
555
- ```javascript
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
- #### Common iOS module patterns
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
- ```objc
567
- #import <CoreLocation/CoreLocation.h>
142
+ ## Install
568
143
 
569
- - (void)startTracking {
570
- CLLocationManager *manager = [[CLLocationManager alloc] init];
571
- [manager setDelegate:self];
572
- [manager startUpdatingLocation];
573
- }
144
+ ```xml
145
+ <modules>
146
+ <module platform="android">hyperloop</module>
147
+ <module platform="iphone">hyperloop</module>
148
+ </modules>
574
149
  ```
575
150
 
576
- Firing events to JavaScript:
151
+ The module is unpacked like any other - see
152
+ [using modules](https://titaniumsdk.com/docs/build/modules.md).
577
153
 
578
- ```objc
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
- View proxies (custom UI components):
156
+ The path is the native type's own name.
589
157
 
590
- ```objc
591
- @interface MyViewProxy : TiViewProxy {
592
- UIView *myView;
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
- - (UIView *)view {
596
- if (myView == nil) {
597
- myView = [[MyCustomView alloc] initWithFrame:[self frame]];
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
- @end
603
- ```
167
+ ## Hyperloop or a module
604
168
 
605
- ### Swift Package Manager dependencies (iOS)
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
- Since Titanium SDK 13.1.0, an iOS module declares Swift package dependencies in an `spm.json` file and the CLI wires them into the consuming app. This replaces vendoring pre-built `.xcframework` files in `ios/platform` and replaces the legacy `hooks/ti.spm.js` hook.
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
- Add the package to your module's Xcode project as usual so the module compiles against it, then declare the same package in `spm.json`, next to the module `manifest`:
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
- ```json
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
- #### Dependency fields
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
- | Field | Required | Default | Description |
640
- |---|---|---|---|
641
- | `repositoryURL` | yes | — | Git URL of the Swift package. A dependency without it is skipped with a warning. |
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
- #### Product fields
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
- | Field | Required | Default | Description |
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
- #### embedded vs host
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
- `host` makes the CLI add the package to the app's Xcode project at build time, so the dependency exists once in the app. Use it when several modules share the same package, or when the package ships resources or dynamic frameworks the app must embed.
200
+ ## CLI plugins
661
201
 
662
- If two modules request the same package, it is added once. If they request conflicting version requirements, the CLI warns and uses the first requirement it sees.
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
- #### Building
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
- ```bash
667
- cd mymodule/ios
668
- ti build -p ios --build-only
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 zip in `dist/` now carries the package information in its `metadata.json`. App developers install the module normally; nothing extra is required of them. During an app build, lines prefixed with `[SPM]` say what was added:
213
+ ## The shape of one
672
214
 
673
- ```
674
- [INFO] [SPM] Will add 1 Swift package(s) to the app project
675
- ```
215
+ ```js
216
+ // plugins/ti.example/hooks/stamp.js
217
+ exports.cliVersion = '>=3.X';
218
+ exports.version = '1.0.0';
676
219
 
677
- #### Migrating off ti.spm.js
678
-
679
- A module that ships both `spm.json` and `hooks/ti.spm.js` has the hook excluded from its packaged zip automatically. A module that still ships only the hook makes the app build log a warning. To migrate, translate the hook's package information into `spm.json`, delete `hooks/ti.spm.js`, and rebuild.
680
-
681
- ### Module distribution
682
-
683
- #### Packaging
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
- Both platforms create a `.zip` file for distribution:
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
- ```bash
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
- # For Android
692
- ant dist
693
- ```
237
+ `priority` orders hooks against each other; the default is 1000.
694
238
 
695
- #### Installing module
239
+ ## Where they are found
696
240
 
697
- 1. Local installation: extract to `/Library/Application Support/Titanium/modules/`
698
- 2. Global installation: use `gittio` or `npm` for publishing
699
- 3. Project-specific: place in the app's `modules/` directory
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
- #### Module configuration in tiapp.xml
247
+ Declare a project plugin in `tiapp.xml`:
702
248
 
703
249
  ```xml
704
- <modules>
705
- <module platform="android" version="1.0.0">com.example.mymodule</module>
706
- <module platform="iphone" version="1.0.0">com.example.mymodule</module>
707
- </modules>
250
+ <plugins>
251
+ <plugin version="1.0">ti.example</plugin>
252
+ </plugins>
708
253
  ```
709
254
 
710
- ## 4. Choosing between Hyperloop and native modules
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
- - Prototyping native API access
715
- - App-specific native functionality (not reusable)
716
- - One-off native API calls
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
- ### Use native modules when
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
- - Creating reusable components
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
- ## 5. Finding and using third-party modules
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
- ### Ti_slack marketplace
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
- Search for existing modules at:
734
- - https://fromzerotoapp.com/modules/
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
- ### Popular third-party modules
284
+ ## Alloy's own hook file
738
285
 
739
- - ti.map - Enhanced mapping (Google Maps, Apple Maps)
740
- - ti.paint - Drawing/signature capture
741
- - ti.barcode - Barcode/QR scanning
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
- ### Using third-party modules
290
+ ## Keep them cheap
748
291
 
749
- 1. Download module zip
750
- 2. Extract to modules directory
751
- 3. Add to `tiapp.xml`:
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
- ```xml
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
- ## Best practices summary
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. Try built-in APIs first; Titanium may already have what you need.
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.