@craft-native/ios 0.0.70
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 +619 -0
- package/dist/cli.js +664 -0
- package/dist/index.js +584 -0
- package/package.json +33 -0
- package/templates/CraftActivityAttributes.swift +14 -0
- package/templates/CraftApp.swift +5169 -0
- package/templates/CraftAppExtensions.swift +361 -0
- package/templates/CraftLiveActivityWidget.swift.template +62 -0
- package/templates/CraftWatchApp.swift.template +155 -0
- package/templates/CraftWidget.swift +247 -0
- package/templates/Info.plist.template +66 -0
- package/templates/WatchApp.Info.plist.template +26 -0
- package/templates/WidgetExtension.Info.plist +31 -0
- package/templates/fastlane/Appfile +45 -0
- package/templates/fastlane/Fastfile +189 -0
- package/templates/fastlane/Gemfile +7 -0
- package/templates/fastlane/Matchfile +58 -0
- package/templates/github-workflow-ios.yml +192 -0
- package/templates/github-workflow-release.yml +175 -0
- package/templates/github-workflow-test.yml +272 -0
- package/templates/project.yml.template +36 -0
- package/templates/test-bridges.html +1463 -0
package/README.md
ADDED
|
@@ -0,0 +1,619 @@
|
|
|
1
|
+
# @craft-native/ios
|
|
2
|
+
|
|
3
|
+
Build native iOS apps with web technologies using Craft.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
### Core
|
|
8
|
+
|
|
9
|
+
- **WKWebView** - Native iOS WebView with full JavaScript support
|
|
10
|
+
- **Safe Areas** - Automatic handling of notch and home indicator
|
|
11
|
+
- **Dark Mode** - Native dark/light theme support
|
|
12
|
+
|
|
13
|
+
### Input & Feedback
|
|
14
|
+
|
|
15
|
+
- **Native Speech Recognition** - iOS SFSpeechRecognizer (works offline!)
|
|
16
|
+
- **Haptic Feedback** - UIImpactFeedbackGenerator, UINotificationFeedbackGenerator
|
|
17
|
+
- **Audio Recording** - AVAudioRecorder with compressed output
|
|
18
|
+
- **Video Recording** - UIImagePickerController video capture
|
|
19
|
+
|
|
20
|
+
### Device Access
|
|
21
|
+
|
|
22
|
+
- **Camera & Gallery** - Take photos or pick from gallery
|
|
23
|
+
- **Barcode/QR Scanner** - VisionKit DataScanner (iOS 16+)
|
|
24
|
+
- **File Picker** - UIDocumentPickerViewController
|
|
25
|
+
- **File Download** - Download and save files to device
|
|
26
|
+
|
|
27
|
+
### Sensors & Location
|
|
28
|
+
|
|
29
|
+
- **Geolocation** - GPS and network location via CLLocationManager
|
|
30
|
+
- **Motion Sensors** - Accelerometer & gyroscope via CoreMotion
|
|
31
|
+
- **NFC** - Read NFC tags via CoreNFC
|
|
32
|
+
|
|
33
|
+
### Communication
|
|
34
|
+
|
|
35
|
+
- **Share Sheet** - Native UIActivityViewController
|
|
36
|
+
- **Clipboard** - Read and write to system clipboard
|
|
37
|
+
- **Push Notifications** - APNs integration
|
|
38
|
+
|
|
39
|
+
### Security & Auth
|
|
40
|
+
|
|
41
|
+
- **Biometric Auth** - Face ID / Touch ID
|
|
42
|
+
- **Social Auth** - Apple Sign In
|
|
43
|
+
- **Secure Storage** - Keychain integration
|
|
44
|
+
|
|
45
|
+
### Data & Storage
|
|
46
|
+
|
|
47
|
+
- **Local Database** - SQLite with full SQL support
|
|
48
|
+
- **Bluetooth LE** - CoreBluetooth scanning
|
|
49
|
+
|
|
50
|
+
### System
|
|
51
|
+
|
|
52
|
+
- **Device Info** - Device model, OS version, screen info
|
|
53
|
+
- **Network Status** - Connection type monitoring (WiFi/Cellular)
|
|
54
|
+
- **App Badge** - Notification badge count
|
|
55
|
+
- **App State** - Foreground/background detection
|
|
56
|
+
- **Flashlight** - Camera flash control
|
|
57
|
+
- **Open URL** - External browser launch
|
|
58
|
+
- **Vibration** - Custom vibration patterns
|
|
59
|
+
- **App Review** - StoreKit review prompt
|
|
60
|
+
- **Screen Capture** - Take screenshots programmatically
|
|
61
|
+
- **Health/Fitness** - HealthKit integration
|
|
62
|
+
|
|
63
|
+
## Installation
|
|
64
|
+
|
|
65
|
+
The iOS support is built into the main `craft` CLI:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
bun add craft-native
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Quick Start
|
|
72
|
+
|
|
73
|
+
### 1. Initialize Project
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
craft ios init MyApp --bundle-id com.example.myapp
|
|
77
|
+
cd ios
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### 2. Add Your Web Content
|
|
81
|
+
|
|
82
|
+
Replace `dist/index.html` with your web app, or point to a dev server:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
craft ios build --html-path ../dist/index.html
|
|
86
|
+
# or
|
|
87
|
+
craft ios build --dev-server http://localhost:3456
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### 3. Open in Xcode
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
craft ios open
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### 4. Run on Device
|
|
97
|
+
|
|
98
|
+
In Xcode:
|
|
99
|
+
|
|
100
|
+
1. Select your Team in Signing & Capabilities
|
|
101
|
+
2. Connect your iPhone
|
|
102
|
+
3. Select your device
|
|
103
|
+
4. Click Run
|
|
104
|
+
|
|
105
|
+
## Configuration
|
|
106
|
+
|
|
107
|
+
Edit `craft.config.json` in your iOS project:
|
|
108
|
+
|
|
109
|
+
```json
|
|
110
|
+
{
|
|
111
|
+
"appName": "MyApp",
|
|
112
|
+
"bundleId": "com.example.myapp",
|
|
113
|
+
"version": "1.0.0",
|
|
114
|
+
"buildNumber": "1",
|
|
115
|
+
"darkMode": true,
|
|
116
|
+
"backgroundColor": "#1a1a2e",
|
|
117
|
+
"enableSpeechRecognition": true,
|
|
118
|
+
"enableHaptics": true,
|
|
119
|
+
"enableShare": true,
|
|
120
|
+
"enableCamera": true,
|
|
121
|
+
"enableBiometric": true,
|
|
122
|
+
"enablePushNotifications": false,
|
|
123
|
+
"enableSecureStorage": true,
|
|
124
|
+
"enableGeolocation": true,
|
|
125
|
+
"enableClipboard": true,
|
|
126
|
+
"enableNetworkStatus": true,
|
|
127
|
+
"enableAppReview": true,
|
|
128
|
+
"enableFlashlight": true,
|
|
129
|
+
"enableQRScanner": true,
|
|
130
|
+
"enableFilePicker": true,
|
|
131
|
+
"enableFileDownload": true,
|
|
132
|
+
"enableSocialAuth": true,
|
|
133
|
+
"enableAudioRecording": true,
|
|
134
|
+
"enableVideoRecording": true,
|
|
135
|
+
"enableMotionSensors": true,
|
|
136
|
+
"enableLocalDatabase": true,
|
|
137
|
+
"enableBluetooth": true,
|
|
138
|
+
"enableNFC": true,
|
|
139
|
+
"enableHealthKit": false,
|
|
140
|
+
"enableScreenCapture": true,
|
|
141
|
+
"iosVersion": "15.0",
|
|
142
|
+
"teamId": ""
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## JavaScript Bridge
|
|
147
|
+
|
|
148
|
+
Once Craft is initialized, the `window.craft` object is available:
|
|
149
|
+
|
|
150
|
+
```javascript
|
|
151
|
+
// Wait for Craft to be ready
|
|
152
|
+
window.addEventListener('craftReady', (e) => {
|
|
153
|
+
console.log('Platform:', e.detail.platform); // 'ios'
|
|
154
|
+
console.log('Capabilities:', e.detail.capabilities);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
// Haptic feedback
|
|
158
|
+
window.craft.haptic('light'); // light, medium, heavy
|
|
159
|
+
window.craft.haptic('success'); // success, warning, error
|
|
160
|
+
window.craft.haptic('selection');
|
|
161
|
+
|
|
162
|
+
// Speech recognition
|
|
163
|
+
window.craft.startListening();
|
|
164
|
+
window.craft.stopListening();
|
|
165
|
+
|
|
166
|
+
// Listen for speech events
|
|
167
|
+
window.addEventListener('craftSpeechStart', () => { /* recording started */ });
|
|
168
|
+
window.addEventListener('craftSpeechResult', (e) => {
|
|
169
|
+
console.log(e.detail.transcript); // "hello world"
|
|
170
|
+
console.log(e.detail.isFinal); // true/false
|
|
171
|
+
});
|
|
172
|
+
window.addEventListener('craftSpeechEnd', () => { /* recording stopped */ });
|
|
173
|
+
window.addEventListener('craftSpeechError', (e) => {
|
|
174
|
+
console.error(e.detail.error);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
// Share
|
|
178
|
+
window.craft.share('Check out this app!', 'My App');
|
|
179
|
+
|
|
180
|
+
// Camera & Gallery
|
|
181
|
+
const imageBase64 = await window.craft.openCamera();
|
|
182
|
+
const selectedImage = await window.craft.pickImage();
|
|
183
|
+
|
|
184
|
+
// Barcode/QR Scanner (iOS 16+)
|
|
185
|
+
const scannedCode = await window.craft.scanQRCode();
|
|
186
|
+
console.log(scannedCode); // "https://example.com"
|
|
187
|
+
|
|
188
|
+
// File Picker
|
|
189
|
+
const file = await window.craft.pickFile(['public.image', 'public.pdf']);
|
|
190
|
+
console.log(file.name, file.data); // base64 encoded
|
|
191
|
+
|
|
192
|
+
// File Download
|
|
193
|
+
await window.craft.downloadFile('https://example.com/file.pdf', 'document.pdf');
|
|
194
|
+
await window.craft.saveFile(base64Data, 'image.png', 'image/png');
|
|
195
|
+
|
|
196
|
+
// Social Auth (Apple Sign In)
|
|
197
|
+
const user = await window.craft.signInWithApple();
|
|
198
|
+
console.log(user.userId, user.email, user.fullName, user.identityToken);
|
|
199
|
+
|
|
200
|
+
// Audio Recording
|
|
201
|
+
await window.craft.startAudioRecording();
|
|
202
|
+
const audioBase64 = await window.craft.stopAudioRecording();
|
|
203
|
+
|
|
204
|
+
// Video Recording
|
|
205
|
+
const videoBase64 = await window.craft.startVideoRecording();
|
|
206
|
+
|
|
207
|
+
// Motion Sensors
|
|
208
|
+
window.craft.startMotionUpdates();
|
|
209
|
+
window.addEventListener('craftMotionUpdate', (e) => {
|
|
210
|
+
console.log('Accelerometer:', e.detail.accelerometer); // {x, y, z}
|
|
211
|
+
console.log('Gyroscope:', e.detail.gyroscope); // {x, y, z}
|
|
212
|
+
});
|
|
213
|
+
window.craft.stopMotionUpdates();
|
|
214
|
+
|
|
215
|
+
// Local Database (SQLite)
|
|
216
|
+
await window.craft.db.execute('CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)');
|
|
217
|
+
await window.craft.db.execute('INSERT INTO users (name) VALUES (?)', ['Alice']);
|
|
218
|
+
const users = await window.craft.db.query('SELECT * FROM users');
|
|
219
|
+
|
|
220
|
+
// Bluetooth LE Scanning
|
|
221
|
+
window.craft.startBluetoothScan();
|
|
222
|
+
window.addEventListener('craftBluetoothDevice', (e) => {
|
|
223
|
+
console.log(e.detail.name, e.detail.uuid, e.detail.rssi);
|
|
224
|
+
});
|
|
225
|
+
window.craft.stopBluetoothScan();
|
|
226
|
+
|
|
227
|
+
// NFC Tag Reading
|
|
228
|
+
const nfcData = await window.craft.scanNFC();
|
|
229
|
+
console.log(nfcData); // Tag content
|
|
230
|
+
|
|
231
|
+
// Health/Fitness (requires HealthKit entitlement)
|
|
232
|
+
await window.craft.requestHealthAuthorization(['stepCount', 'heartRate']);
|
|
233
|
+
const steps = await window.craft.getHealthData('stepCount', startDate, endDate);
|
|
234
|
+
|
|
235
|
+
// Screen Capture
|
|
236
|
+
const screenshotBase64 = await window.craft.takeScreenshot();
|
|
237
|
+
|
|
238
|
+
// Biometric authentication
|
|
239
|
+
try {
|
|
240
|
+
const authenticated = await window.craft.authenticate('Confirm your identity');
|
|
241
|
+
if (authenticated) {
|
|
242
|
+
console.log('User authenticated!');
|
|
243
|
+
}
|
|
244
|
+
} catch (error) {
|
|
245
|
+
console.log('Authentication failed:', error.message);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Push notifications
|
|
249
|
+
const token = await window.craft.registerPush();
|
|
250
|
+
console.log('Push token:', token);
|
|
251
|
+
|
|
252
|
+
// Secure storage (Keychain)
|
|
253
|
+
await window.craft.secureStore.set('api*key', 'secret123');
|
|
254
|
+
const apiKey = await window.craft.secureStore.get('api*key');
|
|
255
|
+
await window.craft.secureStore.remove('api*key');
|
|
256
|
+
|
|
257
|
+
// Geolocation
|
|
258
|
+
const position = await window.craft.getCurrentPosition();
|
|
259
|
+
console.log(position.latitude, position.longitude);
|
|
260
|
+
|
|
261
|
+
// Watch position (continuous updates)
|
|
262
|
+
const watchId = window.craft.watchPosition((position) => {
|
|
263
|
+
console.log('New position:', position);
|
|
264
|
+
});
|
|
265
|
+
window.craft.clearWatch(watchId);
|
|
266
|
+
|
|
267
|
+
// Clipboard
|
|
268
|
+
await window.craft.clipboard.write('Hello World');
|
|
269
|
+
const text = await window.craft.clipboard.read();
|
|
270
|
+
|
|
271
|
+
// Device Info
|
|
272
|
+
const device = window.craft.getDeviceInfo();
|
|
273
|
+
console.log(device.model, device.systemVersion, device.screenWidth);
|
|
274
|
+
|
|
275
|
+
// App Badge
|
|
276
|
+
window.craft.setBadge(5);
|
|
277
|
+
window.craft.clearBadge();
|
|
278
|
+
|
|
279
|
+
// Network Status
|
|
280
|
+
const network = window.craft.getNetworkStatus();
|
|
281
|
+
console.log(network.isConnected, network.type); // true, 'wifi'
|
|
282
|
+
|
|
283
|
+
// Network change listener
|
|
284
|
+
window.craft.onNetworkChange((status) => {
|
|
285
|
+
console.log('Network changed:', status);
|
|
286
|
+
});
|
|
287
|
+
window.craft.offNetworkChange();
|
|
288
|
+
|
|
289
|
+
// App Review (StoreKit)
|
|
290
|
+
await window.craft.requestReview();
|
|
291
|
+
|
|
292
|
+
// Flashlight
|
|
293
|
+
window.craft.setFlashlight(true); // turn on
|
|
294
|
+
window.craft.setFlashlight(false); // turn off
|
|
295
|
+
window.craft.toggleFlashlight(); // toggle
|
|
296
|
+
|
|
297
|
+
// Open URL (external browser)
|
|
298
|
+
window.craft.openURL('https://example.com');
|
|
299
|
+
|
|
300
|
+
// Vibration Pattern (custom durations in ms)
|
|
301
|
+
window.craft.vibrate([100, 50, 100, 50, 200]);
|
|
302
|
+
|
|
303
|
+
// App State
|
|
304
|
+
const state = window.craft.getAppState(); // 'active', 'inactive', 'background'
|
|
305
|
+
|
|
306
|
+
// App state change listener
|
|
307
|
+
window.craft.onAppStateChange((state) => {
|
|
308
|
+
console.log('App state:', state);
|
|
309
|
+
});
|
|
310
|
+
window.craft.offAppStateChange();
|
|
311
|
+
|
|
312
|
+
// Logging (appears in Xcode console)
|
|
313
|
+
window.craft.log('Debug message');
|
|
314
|
+
|
|
315
|
+
// ==================== High Value Bridges ====================
|
|
316
|
+
|
|
317
|
+
// Contacts
|
|
318
|
+
const contacts = await window.craft.getContacts();
|
|
319
|
+
console.log(contacts); // [{id, givenName, familyName, displayName, phoneNumbers, emailAddresses}]
|
|
320
|
+
|
|
321
|
+
const newContactId = await window.craft.addContact({
|
|
322
|
+
givenName: 'John',
|
|
323
|
+
familyName: 'Doe',
|
|
324
|
+
phone: '+1234567890',
|
|
325
|
+
email: 'john@example.com'
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
// Calendar Events
|
|
329
|
+
const events = await window.craft.getCalendarEvents(startDateMs, endDateMs);
|
|
330
|
+
console.log(events); // [{id, title, location, notes, startDate, endDate, isAllDay}]
|
|
331
|
+
|
|
332
|
+
const newEventId = await window.craft.createCalendarEvent({
|
|
333
|
+
title: 'Meeting',
|
|
334
|
+
location: 'Office',
|
|
335
|
+
notes: 'Discuss project',
|
|
336
|
+
startDate: Date.now(),
|
|
337
|
+
endDate: Date.now() + 3600000,
|
|
338
|
+
isAllDay: false
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
await window.craft.deleteCalendarEvent(eventId);
|
|
342
|
+
|
|
343
|
+
// Local Notifications
|
|
344
|
+
const notificationId = await window.craft.scheduleNotification({
|
|
345
|
+
id: 'reminder-1',
|
|
346
|
+
title: 'Reminder',
|
|
347
|
+
body: 'Don\'t forget!',
|
|
348
|
+
badge: 1,
|
|
349
|
+
timestamp: Date.now() + 60000, // 1 minute from now
|
|
350
|
+
// or use delay: 60000 // delay in ms
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
await window.craft.cancelNotification('reminder-1');
|
|
354
|
+
await window.craft.cancelAllNotifications();
|
|
355
|
+
const pending = await window.craft.getPendingNotifications();
|
|
356
|
+
|
|
357
|
+
// In-App Purchase
|
|
358
|
+
const products = await window.craft.getProducts(['product*id*1', 'product*id*2']);
|
|
359
|
+
console.log(products); // [{id, title, description, price, priceLocale}]
|
|
360
|
+
|
|
361
|
+
const purchaseResult = await window.craft.purchase('product*id*1');
|
|
362
|
+
console.log(purchaseResult); // {transactionId, productId, ...}
|
|
363
|
+
|
|
364
|
+
await window.craft.restorePurchases();
|
|
365
|
+
|
|
366
|
+
// Keep Screen Awake
|
|
367
|
+
window.craft.setKeepAwake(true); // Prevent screen dimming
|
|
368
|
+
window.craft.setKeepAwake(false); // Allow screen dimming
|
|
369
|
+
|
|
370
|
+
// Orientation Lock
|
|
371
|
+
window.craft.lockOrientation('portrait'); // Lock to portrait
|
|
372
|
+
window.craft.lockOrientation('landscape'); // Lock to landscape
|
|
373
|
+
window.craft.unlockOrientation(); // Allow all orientations
|
|
374
|
+
|
|
375
|
+
// ==================== Medium Value Bridges ====================
|
|
376
|
+
|
|
377
|
+
// Background Tasks (iOS 13+)
|
|
378
|
+
await window.craft.backgroundTask.register('sync-data');
|
|
379
|
+
await window.craft.backgroundTask.schedule('sync-data', {
|
|
380
|
+
delay: 900, // 15 minutes minimum
|
|
381
|
+
requiresNetwork: true,
|
|
382
|
+
requiresCharging: false
|
|
383
|
+
});
|
|
384
|
+
await window.craft.backgroundTask.cancel('sync-data');
|
|
385
|
+
await window.craft.backgroundTask.cancelAll();
|
|
386
|
+
|
|
387
|
+
// PDF Viewer
|
|
388
|
+
await window.craft.openPDF('https://example.com/document.pdf');
|
|
389
|
+
await window.craft.openPDF(base64PdfData, 5); // Open at page 5
|
|
390
|
+
await window.craft.closePDF();
|
|
391
|
+
|
|
392
|
+
// Contacts Picker (shows native picker UI)
|
|
393
|
+
const contact = await window.craft.pickContact();
|
|
394
|
+
console.log(contact); // {id, givenName, familyName, displayName, phoneNumbers, emailAddresses}
|
|
395
|
+
|
|
396
|
+
const contacts = await window.craft.pickContact({multiple: true}); // Select multiple
|
|
397
|
+
|
|
398
|
+
// App Shortcuts (3D Touch / long press)
|
|
399
|
+
await window.craft.shortcuts.set([
|
|
400
|
+
{type: 'new-message', title: 'New Message', subtitle: 'Start composing', iconName: 'square.and.pencil'},
|
|
401
|
+
{type: 'search', title: 'Search', iconName: 'magnifyingglass'}
|
|
402
|
+
]);
|
|
403
|
+
window.craft.shortcuts.onShortcut((shortcut) => {
|
|
404
|
+
console.log('Shortcut activated:', shortcut.type);
|
|
405
|
+
});
|
|
406
|
+
await window.craft.shortcuts.clear();
|
|
407
|
+
|
|
408
|
+
// Keychain Sharing (cross-app data with access groups)
|
|
409
|
+
await window.craft.sharedKeychain.set('user*token', 'abc123', 'com.example.shared'); // group optional
|
|
410
|
+
const result = await window.craft.sharedKeychain.get('user*token', 'com.example.shared');
|
|
411
|
+
console.log(result.value); // 'abc123'
|
|
412
|
+
await window.craft.sharedKeychain.remove('user*token');
|
|
413
|
+
|
|
414
|
+
// Local Auth Persistence (skip re-auth for a duration)
|
|
415
|
+
await window.craft.authPersistence.enable(300); // 5 minutes
|
|
416
|
+
const status = await window.craft.authPersistence.check();
|
|
417
|
+
if (status.isValid) {
|
|
418
|
+
console.log('Session valid for', status.remainingSeconds, 'more seconds');
|
|
419
|
+
}
|
|
420
|
+
await window.craft.authPersistence.clear();
|
|
421
|
+
|
|
422
|
+
// ==================== Nice to Have Bridges ====================
|
|
423
|
+
|
|
424
|
+
// AR (ARKit) - Requires iOS device with A9+ chip
|
|
425
|
+
await window.craft.ar.start({planeDetection: true});
|
|
426
|
+
window.craft.ar.onPlaneDetected((plane) => {
|
|
427
|
+
console.log('Plane detected:', plane.id, plane.alignment);
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
// Place 3D objects (built-in shapes or .usdz/.scn files)
|
|
431
|
+
const obj = await window.craft.ar.placeObject('box', {x: 0, y: 0, z: -0.5});
|
|
432
|
+
console.log('Object placed:', obj.objectId);
|
|
433
|
+
// Built-in shapes: 'box', 'sphere', 'cylinder', 'cone'
|
|
434
|
+
// Or provide URL to .usdz or .scn file
|
|
435
|
+
|
|
436
|
+
// Get detected planes
|
|
437
|
+
const planes = await window.craft.ar.getPlanes();
|
|
438
|
+
console.log(planes); // [{id, alignment, center, extent}]
|
|
439
|
+
|
|
440
|
+
// Remove object
|
|
441
|
+
await window.craft.ar.removeObject(obj.objectId);
|
|
442
|
+
|
|
443
|
+
// Stop AR session
|
|
444
|
+
await window.craft.ar.stop();
|
|
445
|
+
|
|
446
|
+
// ML (Vision Framework)
|
|
447
|
+
// First capture an image
|
|
448
|
+
const image = await window.craft.openCamera();
|
|
449
|
+
|
|
450
|
+
// Image Classification - Identify what's in the image
|
|
451
|
+
const labels = await window.craft.ml.classifyImage(image);
|
|
452
|
+
console.log(labels); // [{label: 'cat', confidence: 0.95}, ...]
|
|
453
|
+
|
|
454
|
+
// Object Detection - Detect and locate objects
|
|
455
|
+
const objects = await window.craft.ml.detectObjects(image);
|
|
456
|
+
console.log(objects); // [{labels: [...], boundingBox: {x, y, width, height}}]
|
|
457
|
+
|
|
458
|
+
// Text Recognition (OCR) - Extract text from image
|
|
459
|
+
const textResults = await window.craft.ml.recognizeText(image);
|
|
460
|
+
console.log(textResults); // [{text: 'Hello World', confidence: 0.98, boundingBox: {...}}]
|
|
461
|
+
|
|
462
|
+
// ==================== Widgets (WidgetKit) ====================
|
|
463
|
+
|
|
464
|
+
// Update widget data - displayed on home screen widget
|
|
465
|
+
await window.craft.widget.update({
|
|
466
|
+
title: 'My App',
|
|
467
|
+
subtitle: 'Latest update',
|
|
468
|
+
value: '42',
|
|
469
|
+
icon: 'star.fill' // SF Symbol name
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
// Reload all widgets
|
|
473
|
+
await window.craft.widget.reload();
|
|
474
|
+
|
|
475
|
+
// ==================== Siri Shortcuts ====================
|
|
476
|
+
|
|
477
|
+
// Register a Siri shortcut
|
|
478
|
+
await window.craft.siri.register('Open my app', 'open*app');
|
|
479
|
+
|
|
480
|
+
// Remove a Siri shortcut
|
|
481
|
+
await window.craft.siri.remove('open*app');
|
|
482
|
+
|
|
483
|
+
// Listen for Siri shortcut invocations
|
|
484
|
+
window.craft.siri.onInvoke((detail) => {
|
|
485
|
+
console.log('Siri invoked:', detail.action);
|
|
486
|
+
});
|
|
487
|
+
|
|
488
|
+
// ==================== Watch Connectivity ====================
|
|
489
|
+
|
|
490
|
+
// Check if watch is reachable
|
|
491
|
+
const status = await window.craft.watch.isReachable();
|
|
492
|
+
console.log(status.reachable); // true/false
|
|
493
|
+
|
|
494
|
+
// Send message to watch
|
|
495
|
+
const reply = await window.craft.watch.send({
|
|
496
|
+
action: 'ping',
|
|
497
|
+
data: { timestamp: Date.now() }
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
// Update application context (synced to watch)
|
|
501
|
+
await window.craft.watch.updateContext({
|
|
502
|
+
lastUpdate: Date.now(),
|
|
503
|
+
status: 'active'
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
// Listen for messages from watch
|
|
507
|
+
window.craft.watch.onMessage((message) => {
|
|
508
|
+
console.log('Watch message:', message);
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
// Listen for watch reachability changes
|
|
512
|
+
window.craft.watch.onReachabilityChange((status) => {
|
|
513
|
+
console.log('Watch reachable:', status.reachable);
|
|
514
|
+
});
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
## CLI Reference
|
|
518
|
+
|
|
519
|
+
```bash
|
|
520
|
+
craft ios init <name> # Initialize new iOS project
|
|
521
|
+
craft ios build # Build and generate Xcode project
|
|
522
|
+
craft ios open # Open Xcode project
|
|
523
|
+
craft ios run --simulator # Run on iOS Simulator
|
|
524
|
+
|
|
525
|
+
Options:
|
|
526
|
+
--bundle-id <id> Bundle identifier
|
|
527
|
+
--team-id <id> Apple Developer Team ID
|
|
528
|
+
--html-path <path> Path to HTML file
|
|
529
|
+
-d, --dev-server <url> Development server URL
|
|
530
|
+
-o, --output <dir> Output directory (default: ./ios)
|
|
531
|
+
-s, --simulator Run on simulator
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
## Requirements
|
|
535
|
+
|
|
536
|
+
- macOS with Xcode 15+
|
|
537
|
+
- [xcodegen](https://github.com/yonaskolb/XcodeGen): `brew install xcodegen`
|
|
538
|
+
- Apple Developer account (free or paid)
|
|
539
|
+
- iPhone running iOS 15+ (for device deployment)
|
|
540
|
+
|
|
541
|
+
## Permissions
|
|
542
|
+
|
|
543
|
+
Add to your Info.plist as needed:
|
|
544
|
+
|
|
545
|
+
```xml
|
|
546
|
+
<!-- Speech Recognition -->
|
|
547
|
+
<key>NSSpeechRecognitionUsageDescription</key>
|
|
548
|
+
<string>This app uses speech recognition for voice commands.</string>
|
|
549
|
+
<key>NSMicrophoneUsageDescription</key>
|
|
550
|
+
<string>This app needs microphone access for speech recognition.</string>
|
|
551
|
+
|
|
552
|
+
<!-- Camera -->
|
|
553
|
+
<key>NSCameraUsageDescription</key>
|
|
554
|
+
<string>This app needs camera access to take photos.</string>
|
|
555
|
+
<key>NSPhotoLibraryUsageDescription</key>
|
|
556
|
+
<string>This app needs photo library access.</string>
|
|
557
|
+
|
|
558
|
+
<!-- Location -->
|
|
559
|
+
<key>NSLocationWhenInUseUsageDescription</key>
|
|
560
|
+
<string>This app needs your location.</string>
|
|
561
|
+
|
|
562
|
+
<!-- Face ID -->
|
|
563
|
+
<key>NSFaceIDUsageDescription</key>
|
|
564
|
+
<string>This app uses Face ID for authentication.</string>
|
|
565
|
+
|
|
566
|
+
<!-- NFC -->
|
|
567
|
+
<key>NFCReaderUsageDescription</key>
|
|
568
|
+
<string>This app reads NFC tags.</string>
|
|
569
|
+
|
|
570
|
+
<!-- Bluetooth -->
|
|
571
|
+
<key>NSBluetoothAlwaysUsageDescription</key>
|
|
572
|
+
<string>This app uses Bluetooth.</string>
|
|
573
|
+
|
|
574
|
+
<!-- Health -->
|
|
575
|
+
<key>NSHealthShareUsageDescription</key>
|
|
576
|
+
<string>This app reads health data.</string>
|
|
577
|
+
<key>NSHealthUpdateUsageDescription</key>
|
|
578
|
+
<string>This app writes health data.</string>
|
|
579
|
+
|
|
580
|
+
<!-- Contacts -->
|
|
581
|
+
<key>NSContactsUsageDescription</key>
|
|
582
|
+
<string>This app accesses your contacts.</string>
|
|
583
|
+
|
|
584
|
+
<!-- Calendar -->
|
|
585
|
+
<key>NSCalendarsUsageDescription</key>
|
|
586
|
+
<string>This app accesses your calendar.</string>
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
## Development Mode
|
|
590
|
+
|
|
591
|
+
For hot-reload during development:
|
|
592
|
+
|
|
593
|
+
```bash
|
|
594
|
+
# Terminal 1: Start your dev server
|
|
595
|
+
bun run dev # e.g., http://localhost:3456
|
|
596
|
+
|
|
597
|
+
# Terminal 2: Build iOS with dev server
|
|
598
|
+
craft ios build --dev-server http://localhost:3456
|
|
599
|
+
craft ios open
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
The app will load from your dev server instead of bundled HTML.
|
|
603
|
+
|
|
604
|
+
## Publishing
|
|
605
|
+
|
|
606
|
+
Build for App Store:
|
|
607
|
+
|
|
608
|
+
```bash
|
|
609
|
+
craft publish --ios
|
|
610
|
+
```
|
|
611
|
+
|
|
612
|
+
This will:
|
|
613
|
+
|
|
614
|
+
1. Build a release archive
|
|
615
|
+
2. Output path for manual upload to App Store Connect
|
|
616
|
+
|
|
617
|
+
## License
|
|
618
|
+
|
|
619
|
+
MIT
|