@craft-native/ios 0.0.368 → 0.0.374
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 +48 -3
- package/dist/cli.js +314 -158
- package/dist/index.d.ts +6 -2
- package/dist/index.js +338 -179
- package/dist/runtime-profile.d.ts +13 -0
- package/package.json +1 -1
- package/templates/CraftApp.swift +279 -3
- package/templates/CraftBluetooth.swift +12 -0
- package/templates/CraftHealth.swift +124 -8
- package/templates/CraftHeartRate.swift +13 -0
- package/templates/CraftHybrid.swift +52 -11
- package/templates/CraftLiveActivities.swift +2 -0
- package/templates/CraftMusic.swift +23 -0
- package/templates/CraftNativeActions.swift +54 -8
- package/templates/CraftNativeFiles.swift +7 -0
- package/templates/CraftNativeScreen.swift +155 -0
- package/templates/CraftNativeServices.swift +19 -13
- package/templates/CraftVoiceRecording.swift +13 -1
package/templates/CraftApp.swift
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import SwiftUI
|
|
2
2
|
import WebKit
|
|
3
|
+
#if CRAFT_PERMISSION_SPEECH
|
|
3
4
|
import Speech
|
|
5
|
+
#endif
|
|
4
6
|
import AVFoundation
|
|
5
7
|
import LocalAuthentication
|
|
6
8
|
import Security
|
|
@@ -761,6 +763,8 @@ final class CraftChrome: ObservableObject {
|
|
|
761
763
|
var displayTitle: String { titleOverride ?? title }
|
|
762
764
|
var accessibilityLabel: String? = nil
|
|
763
765
|
var image: String? = nil
|
|
766
|
+
/// A compact floating icon, instead of the system full-width accessory shelf.
|
|
767
|
+
var iconOnly = false
|
|
764
768
|
}
|
|
765
769
|
|
|
766
770
|
@Published var tabs: [Tab] = []
|
|
@@ -895,7 +899,8 @@ final class CraftChrome: ObservableObject {
|
|
|
895
899
|
let title = item["title"] as? String {
|
|
896
900
|
next = Tab(id: path, title: title, symbol: item["symbol"] as? String ?? "message",
|
|
897
901
|
badge: (item["badge"] as? String).flatMap { $0.isEmpty ? nil : String($0.prefix(8)) },
|
|
898
|
-
accessibilityLabel: item["accessibilityLabel"] as? String
|
|
902
|
+
accessibilityLabel: item["accessibilityLabel"] as? String,
|
|
903
|
+
iconOnly: item["iconOnly"] as? Bool ?? false)
|
|
899
904
|
} else {
|
|
900
905
|
next = nil
|
|
901
906
|
}
|
|
@@ -2259,9 +2264,11 @@ struct CraftWebView: UIViewRepresentable {
|
|
|
2259
2264
|
// MARK: - Coordinator (Native Bridge)
|
|
2260
2265
|
class Coordinator: NSObject, WKNavigationDelegate, WKScriptMessageHandler, UIImagePickerControllerDelegate, UINavigationControllerDelegate, CLLocationManagerDelegate, ARSCNViewDelegate {
|
|
2261
2266
|
let config: CraftConfig
|
|
2267
|
+
#if CRAFT_PERMISSION_SPEECH
|
|
2262
2268
|
private var speechRecognizer: SFSpeechRecognizer?
|
|
2263
2269
|
private var recognitionRequest: SFSpeechAudioBufferRecognitionRequest?
|
|
2264
2270
|
private var recognitionTask: SFSpeechRecognitionTask?
|
|
2271
|
+
#endif
|
|
2265
2272
|
private var audioEngine = AVAudioEngine()
|
|
2266
2273
|
private weak var webView: WKWebView?
|
|
2267
2274
|
private var pendingCallbackId: String?
|
|
@@ -2392,9 +2399,12 @@ struct CraftWebView: UIViewRepresentable {
|
|
|
2392
2399
|
self.recordingDirectory = recordingDirectory
|
|
2393
2400
|
self.config = config
|
|
2394
2401
|
super.init()
|
|
2402
|
+
#if CRAFT_PERMISSION_SPEECH
|
|
2395
2403
|
if config.enableSpeechRecognition {
|
|
2396
2404
|
speechRecognizer = SFSpeechRecognizer(locale: Locale(identifier: "en-US"))
|
|
2397
2405
|
}
|
|
2406
|
+
#endif
|
|
2407
|
+
#if CRAFT_PERMISSION_LOCATION
|
|
2398
2408
|
if config.enableGeolocation {
|
|
2399
2409
|
locationManager = suppliedLocationManager ?? CLLocationManager()
|
|
2400
2410
|
locationManager?.desiredAccuracy = kCLLocationAccuracyBest
|
|
@@ -2408,15 +2418,22 @@ struct CraftWebView: UIViewRepresentable {
|
|
|
2408
2418
|
catch { locationRecordingRestoreError = error }
|
|
2409
2419
|
}
|
|
2410
2420
|
}
|
|
2421
|
+
#endif
|
|
2422
|
+
#if CRAFT_PERMISSION_CONTACTS
|
|
2411
2423
|
if config.enableContacts {
|
|
2412
2424
|
contactStore = CNContactStore()
|
|
2413
2425
|
}
|
|
2426
|
+
#endif
|
|
2427
|
+
#if CRAFT_PERMISSION_CALENDAR
|
|
2414
2428
|
if config.enableCalendar {
|
|
2415
2429
|
eventStore = EKEventStore()
|
|
2416
2430
|
}
|
|
2431
|
+
#endif
|
|
2432
|
+
#if CRAFT_PERMISSION_MOTION
|
|
2417
2433
|
if config.enableMotionSensors {
|
|
2418
2434
|
motionManager = CMMotionManager()
|
|
2419
2435
|
}
|
|
2436
|
+
#endif
|
|
2420
2437
|
if config.enableHealthKit {
|
|
2421
2438
|
// Apple Health's heart rate as it arrives
|
|
2422
2439
|
// (craft.health.startHeartRateUpdates) reaches the page, and
|
|
@@ -3377,6 +3394,12 @@ struct CraftWebView: UIViewRepresentable {
|
|
|
3377
3394
|
} else {
|
|
3378
3395
|
rejectCallback(callbackId, error: "HealthKit is disabled", code: "CAPABILITY_DISABLED")
|
|
3379
3396
|
}
|
|
3397
|
+
case "discardHealthWorkout":
|
|
3398
|
+
if config.enableHealthKit {
|
|
3399
|
+
CraftHealth.shared.discardWorkout { [weak self] in self?.answer(callbackId, $0) }
|
|
3400
|
+
} else {
|
|
3401
|
+
rejectCallback(callbackId, error: "HealthKit is disabled", code: "CAPABILITY_DISABLED")
|
|
3402
|
+
}
|
|
3380
3403
|
|
|
3381
3404
|
// MARK: - Live Activities
|
|
3382
3405
|
case "isLiveActivityAvailable":
|
|
@@ -4120,11 +4143,13 @@ struct CraftWebView: UIViewRepresentable {
|
|
|
4120
4143
|
/// The bridge, as the source the page runs. Only in a trusted page:
|
|
4121
4144
|
/// the check is the first thing it does.
|
|
4122
4145
|
func bridgeScript() -> String {
|
|
4123
|
-
let laContext = LAContext()
|
|
4124
4146
|
var biometricAvailable = false
|
|
4147
|
+
#if CRAFT_PERMISSION_BIOMETRIC
|
|
4148
|
+
let laContext = LAContext()
|
|
4125
4149
|
if laContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil) {
|
|
4126
4150
|
biometricAvailable = true
|
|
4127
4151
|
}
|
|
4152
|
+
#endif
|
|
4128
4153
|
|
|
4129
4154
|
let script = """
|
|
4130
4155
|
window.craft = {
|
|
@@ -5980,7 +6005,8 @@ struct CraftWebView: UIViewRepresentable {
|
|
|
5980
6005
|
startWorkout: function(options) { return craft._invoke('startHealthWorkout', options || {}); },
|
|
5981
6006
|
pauseWorkout: function() { return craft._invoke('pauseHealthWorkout'); },
|
|
5982
6007
|
resumeWorkout: function() { return craft._invoke('resumeHealthWorkout'); },
|
|
5983
|
-
endWorkout: function() { return craft._invoke('endHealthWorkout'); }
|
|
6008
|
+
endWorkout: function() { return craft._invoke('endHealthWorkout'); },
|
|
6009
|
+
discardWorkout: function() { return craft._invoke('discardHealthWorkout'); }
|
|
5984
6010
|
};
|
|
5985
6011
|
// The Music app's song and transport. Nothing is read until
|
|
5986
6012
|
// requestAuthorization() has been allowed; observe(true) makes
|
|
@@ -6340,6 +6366,7 @@ struct CraftWebView: UIViewRepresentable {
|
|
|
6340
6366
|
|
|
6341
6367
|
// MARK: - Speech Recognition
|
|
6342
6368
|
private func startSpeechRecognition() {
|
|
6369
|
+
#if CRAFT_PERMISSION_SPEECH
|
|
6343
6370
|
SFSpeechRecognizer.requestAuthorization { [weak self] status in
|
|
6344
6371
|
guard status == .authorized else {
|
|
6345
6372
|
self?.sendToWeb("craftSpeechError", data: ["error": "Not authorized"])
|
|
@@ -6347,9 +6374,14 @@ struct CraftWebView: UIViewRepresentable {
|
|
|
6347
6374
|
}
|
|
6348
6375
|
DispatchQueue.main.async { self?.beginRecording() }
|
|
6349
6376
|
}
|
|
6377
|
+
|
|
6378
|
+
#else
|
|
6379
|
+
sendToWeb("craftSpeechError", data: ["error": "Speech recognition was not enabled when this app was built"])
|
|
6380
|
+
#endif
|
|
6350
6381
|
}
|
|
6351
6382
|
|
|
6352
6383
|
private func beginRecording() {
|
|
6384
|
+
#if CRAFT_PERMISSION_SPEECH
|
|
6353
6385
|
if recognitionTask != nil {
|
|
6354
6386
|
recognitionTask?.cancel()
|
|
6355
6387
|
recognitionTask = nil
|
|
@@ -6401,9 +6433,14 @@ struct CraftWebView: UIViewRepresentable {
|
|
|
6401
6433
|
} catch {
|
|
6402
6434
|
sendToWeb("craftSpeechError", data: ["error": "Audio engine failed"])
|
|
6403
6435
|
}
|
|
6436
|
+
|
|
6437
|
+
#else
|
|
6438
|
+
|
|
6439
|
+
#endif
|
|
6404
6440
|
}
|
|
6405
6441
|
|
|
6406
6442
|
private func stopSpeechRecognition() {
|
|
6443
|
+
#if CRAFT_PERMISSION_SPEECH
|
|
6407
6444
|
audioEngine.stop()
|
|
6408
6445
|
audioEngine.inputNode.removeTap(onBus: 0)
|
|
6409
6446
|
recognitionRequest?.endAudio()
|
|
@@ -6412,6 +6449,10 @@ struct CraftWebView: UIViewRepresentable {
|
|
|
6412
6449
|
recognitionTask = nil
|
|
6413
6450
|
sendToWeb("craftSpeechEnd", data: [:])
|
|
6414
6451
|
CraftNativeActions.triggerHaptic(style: "light")
|
|
6452
|
+
|
|
6453
|
+
#else
|
|
6454
|
+
|
|
6455
|
+
#endif
|
|
6415
6456
|
}
|
|
6416
6457
|
|
|
6417
6458
|
// MARK: - Share
|
|
@@ -6453,6 +6494,7 @@ struct CraftWebView: UIViewRepresentable {
|
|
|
6453
6494
|
|
|
6454
6495
|
// MARK: - Camera & Photo Library
|
|
6455
6496
|
private func openCamera() {
|
|
6497
|
+
#if CRAFT_PERMISSION_CAMERA
|
|
6456
6498
|
DispatchQueue.main.async {
|
|
6457
6499
|
guard UIImagePickerController.isSourceTypeAvailable(.camera) else {
|
|
6458
6500
|
self.rejectCallback(self.pendingCallbackId, error: "Camera not available")
|
|
@@ -6465,9 +6507,14 @@ struct CraftWebView: UIViewRepresentable {
|
|
|
6465
6507
|
picker.delegate = self
|
|
6466
6508
|
rootVC.present(picker, animated: true)
|
|
6467
6509
|
}
|
|
6510
|
+
|
|
6511
|
+
#else
|
|
6512
|
+
rejectCallback(pendingCallbackId, error: "Capability was not enabled when this app was built", code: "CAPABILITY_DISABLED")
|
|
6513
|
+
#endif
|
|
6468
6514
|
}
|
|
6469
6515
|
|
|
6470
6516
|
private func pickImage() {
|
|
6517
|
+
#if CRAFT_PERMISSION_PHOTOS
|
|
6471
6518
|
DispatchQueue.main.async {
|
|
6472
6519
|
guard let rootVC = CraftPresenter.topViewController() else { return }
|
|
6473
6520
|
|
|
@@ -6476,6 +6523,10 @@ struct CraftWebView: UIViewRepresentable {
|
|
|
6476
6523
|
picker.delegate = self
|
|
6477
6524
|
rootVC.present(picker, animated: true)
|
|
6478
6525
|
}
|
|
6526
|
+
|
|
6527
|
+
#else
|
|
6528
|
+
rejectCallback(pendingCallbackId, error: "Capability was not enabled when this app was built", code: "CAPABILITY_DISABLED")
|
|
6529
|
+
#endif
|
|
6479
6530
|
}
|
|
6480
6531
|
|
|
6481
6532
|
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
|
|
@@ -6513,6 +6564,7 @@ struct CraftWebView: UIViewRepresentable {
|
|
|
6513
6564
|
|
|
6514
6565
|
// MARK: - Biometric Authentication
|
|
6515
6566
|
private func authenticate(reason: String, callbackId: String?) {
|
|
6567
|
+
#if CRAFT_PERMISSION_BIOMETRIC
|
|
6516
6568
|
let context = LAContext()
|
|
6517
6569
|
var error: NSError?
|
|
6518
6570
|
|
|
@@ -6529,6 +6581,10 @@ struct CraftWebView: UIViewRepresentable {
|
|
|
6529
6581
|
} else {
|
|
6530
6582
|
rejectCallback(callbackId, error: error?.localizedDescription ?? "Biometric not available")
|
|
6531
6583
|
}
|
|
6584
|
+
|
|
6585
|
+
#else
|
|
6586
|
+
rejectCallback(callbackId, error: "Capability was not enabled when this app was built", code: "CAPABILITY_DISABLED")
|
|
6587
|
+
#endif
|
|
6532
6588
|
}
|
|
6533
6589
|
|
|
6534
6590
|
// MARK: - Push Notifications
|
|
@@ -6661,31 +6717,63 @@ struct CraftWebView: UIViewRepresentable {
|
|
|
6661
6717
|
}
|
|
6662
6718
|
switch permission {
|
|
6663
6719
|
case "location", "locationAlways":
|
|
6720
|
+
#if CRAFT_PERMISSION_LOCATION
|
|
6664
6721
|
let status = (locationManager ?? CLLocationManager()).authorizationStatus
|
|
6665
6722
|
let granted = status == .authorizedAlways || (permission == "location" && status == .authorizedWhenInUse)
|
|
6666
6723
|
resolveCallback(callbackId, result: permissionStatus(granted, denied: status == .denied, restricted: status == .restricted))
|
|
6724
|
+
#else
|
|
6725
|
+
rejectCallback(callbackId, error: "Capability was not enabled when this app was built", code: "CAPABILITY_DISABLED")
|
|
6726
|
+
#endif
|
|
6667
6727
|
case "camera":
|
|
6728
|
+
#if CRAFT_PERMISSION_CAMERA
|
|
6668
6729
|
let status = AVCaptureDevice.authorizationStatus(for: .video)
|
|
6669
6730
|
resolveCallback(callbackId, result: permissionStatus(status == .authorized, denied: status == .denied, restricted: status == .restricted))
|
|
6731
|
+
#else
|
|
6732
|
+
rejectCallback(callbackId, error: "Capability was not enabled when this app was built", code: "CAPABILITY_DISABLED")
|
|
6733
|
+
#endif
|
|
6670
6734
|
case "microphone":
|
|
6735
|
+
#if CRAFT_PERMISSION_MICROPHONE
|
|
6671
6736
|
let status = AVAudioSession.sharedInstance().recordPermission
|
|
6672
6737
|
resolveCallback(callbackId, result: permissionStatus(status == .granted, denied: status == .denied))
|
|
6738
|
+
#else
|
|
6739
|
+
rejectCallback(callbackId, error: "Capability was not enabled when this app was built", code: "CAPABILITY_DISABLED")
|
|
6740
|
+
#endif
|
|
6673
6741
|
case "photos":
|
|
6742
|
+
#if CRAFT_PERMISSION_PHOTOS
|
|
6674
6743
|
let status = PHPhotoLibrary.authorizationStatus(for: .readWrite)
|
|
6675
6744
|
resolveCallback(callbackId, result: permissionStatus(status == .authorized || status == .limited, denied: status == .denied, restricted: status == .restricted))
|
|
6745
|
+
#else
|
|
6746
|
+
rejectCallback(callbackId, error: "Capability was not enabled when this app was built", code: "CAPABILITY_DISABLED")
|
|
6747
|
+
#endif
|
|
6676
6748
|
case "contacts":
|
|
6749
|
+
#if CRAFT_PERMISSION_CONTACTS
|
|
6677
6750
|
let status = CNContactStore.authorizationStatus(for: .contacts)
|
|
6678
6751
|
resolveCallback(callbackId, result: permissionStatus(status == .authorized, denied: status == .denied, restricted: status == .restricted))
|
|
6752
|
+
#else
|
|
6753
|
+
rejectCallback(callbackId, error: "Capability was not enabled when this app was built", code: "CAPABILITY_DISABLED")
|
|
6754
|
+
#endif
|
|
6679
6755
|
case "calendar", "reminders":
|
|
6756
|
+
#if CRAFT_PERMISSION_CALENDAR
|
|
6680
6757
|
let entity: EKEntityType = permission == "calendar" ? .event : .reminder
|
|
6681
6758
|
let status = EKEventStore.authorizationStatus(for: entity)
|
|
6682
6759
|
resolveCallback(callbackId, result: permissionStatus(status == .authorized || status.rawValue >= 4, denied: status == .denied, restricted: status == .restricted))
|
|
6760
|
+
#else
|
|
6761
|
+
rejectCallback(callbackId, error: "Capability was not enabled when this app was built", code: "CAPABILITY_DISABLED")
|
|
6762
|
+
#endif
|
|
6683
6763
|
case "motion":
|
|
6764
|
+
#if CRAFT_PERMISSION_MOTION
|
|
6684
6765
|
let status = CMMotionActivityManager.authorizationStatus()
|
|
6685
6766
|
resolveCallback(callbackId, result: permissionStatus(status == .authorized, denied: status == .denied, restricted: status == .restricted))
|
|
6767
|
+
#else
|
|
6768
|
+
rejectCallback(callbackId, error: "Capability was not enabled when this app was built", code: "CAPABILITY_DISABLED")
|
|
6769
|
+
#endif
|
|
6686
6770
|
case "bluetooth":
|
|
6771
|
+
#if CRAFT_PERMISSION_BLUETOOTH
|
|
6687
6772
|
let status = CBManager.authorization
|
|
6688
6773
|
resolveCallback(callbackId, result: permissionStatus(status == .allowedAlways, denied: status == .denied, restricted: status == .restricted))
|
|
6774
|
+
#else
|
|
6775
|
+
rejectCallback(callbackId, error: "Capability was not enabled when this app was built", code: "CAPABILITY_DISABLED")
|
|
6776
|
+
#endif
|
|
6689
6777
|
case "notifications":
|
|
6690
6778
|
// Zig answers UnknownAction for this permission, so Swift
|
|
6691
6779
|
// serves it on both runtimes and this is the only answer.
|
|
@@ -6719,6 +6807,7 @@ struct CraftWebView: UIViewRepresentable {
|
|
|
6719
6807
|
}
|
|
6720
6808
|
switch permission {
|
|
6721
6809
|
case "location", "locationAlways":
|
|
6810
|
+
#if CRAFT_PERMISSION_LOCATION
|
|
6722
6811
|
guard let manager = locationManager else {
|
|
6723
6812
|
rejectCallback(callbackId, error: "Geolocation is disabled", code: "CAPABILITY_DISABLED")
|
|
6724
6813
|
return
|
|
@@ -6741,23 +6830,43 @@ struct CraftWebView: UIViewRepresentable {
|
|
|
6741
6830
|
} else {
|
|
6742
6831
|
manager.requestWhenInUseAuthorization()
|
|
6743
6832
|
}
|
|
6833
|
+
#else
|
|
6834
|
+
rejectCallback(callbackId, error: "Capability was not enabled when this app was built", code: "CAPABILITY_DISABLED")
|
|
6835
|
+
#endif
|
|
6744
6836
|
case "camera":
|
|
6837
|
+
#if CRAFT_PERMISSION_CAMERA
|
|
6745
6838
|
AVCaptureDevice.requestAccess(for: .video) { granted in
|
|
6746
6839
|
self.resolveCallback(callbackId, result: granted ? "granted" : "denied")
|
|
6747
6840
|
}
|
|
6841
|
+
#else
|
|
6842
|
+
rejectCallback(callbackId, error: "Capability was not enabled when this app was built", code: "CAPABILITY_DISABLED")
|
|
6843
|
+
#endif
|
|
6748
6844
|
case "microphone":
|
|
6845
|
+
#if CRAFT_PERMISSION_MICROPHONE
|
|
6749
6846
|
AVAudioSession.sharedInstance().requestRecordPermission { granted in
|
|
6750
6847
|
self.resolveCallback(callbackId, result: granted ? "granted" : "denied")
|
|
6751
6848
|
}
|
|
6849
|
+
#else
|
|
6850
|
+
rejectCallback(callbackId, error: "Capability was not enabled when this app was built", code: "CAPABILITY_DISABLED")
|
|
6851
|
+
#endif
|
|
6752
6852
|
case "photos":
|
|
6853
|
+
#if CRAFT_PERMISSION_PHOTOS
|
|
6753
6854
|
PHPhotoLibrary.requestAuthorization(for: .readWrite) { status in
|
|
6754
6855
|
self.resolveCallback(callbackId, result: status == .authorized || status == .limited ? "granted" : "denied")
|
|
6755
6856
|
}
|
|
6857
|
+
#else
|
|
6858
|
+
rejectCallback(callbackId, error: "Capability was not enabled when this app was built", code: "CAPABILITY_DISABLED")
|
|
6859
|
+
#endif
|
|
6756
6860
|
case "contacts":
|
|
6861
|
+
#if CRAFT_PERMISSION_CONTACTS
|
|
6757
6862
|
(contactStore ?? CNContactStore()).requestAccess(for: .contacts) { granted, _ in
|
|
6758
6863
|
self.resolveCallback(callbackId, result: granted ? "granted" : "denied")
|
|
6759
6864
|
}
|
|
6865
|
+
#else
|
|
6866
|
+
rejectCallback(callbackId, error: "Capability was not enabled when this app was built", code: "CAPABILITY_DISABLED")
|
|
6867
|
+
#endif
|
|
6760
6868
|
case "calendar", "reminders":
|
|
6869
|
+
#if CRAFT_PERMISSION_CALENDAR
|
|
6761
6870
|
let store = eventStore ?? EKEventStore()
|
|
6762
6871
|
let entity: EKEntityType = permission == "calendar" ? .event : .reminder
|
|
6763
6872
|
if #available(iOS 17.0, *) {
|
|
@@ -6775,6 +6884,9 @@ struct CraftWebView: UIViewRepresentable {
|
|
|
6775
6884
|
self.resolveCallback(callbackId, result: granted ? "granted" : "denied")
|
|
6776
6885
|
}
|
|
6777
6886
|
}
|
|
6887
|
+
#else
|
|
6888
|
+
rejectCallback(callbackId, error: "Capability was not enabled when this app was built", code: "CAPABILITY_DISABLED")
|
|
6889
|
+
#endif
|
|
6778
6890
|
case "notifications":
|
|
6779
6891
|
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { granted, _ in
|
|
6780
6892
|
self.resolveCallback(callbackId, result: granted ? "granted" : "denied")
|
|
@@ -6814,6 +6926,7 @@ struct CraftWebView: UIViewRepresentable {
|
|
|
6814
6926
|
}
|
|
6815
6927
|
|
|
6816
6928
|
private func continueSingleLocationRequest(_ manager: CLLocationManager) {
|
|
6929
|
+
#if CRAFT_PERMISSION_LOCATION
|
|
6817
6930
|
guard let callbackId = singleLocationCallbackId else { return }
|
|
6818
6931
|
switch manager.authorizationStatus {
|
|
6819
6932
|
case .notDetermined:
|
|
@@ -6846,6 +6959,10 @@ struct CraftWebView: UIViewRepresentable {
|
|
|
6846
6959
|
singleLocationTimeoutWorkItem = timeoutWorkItem
|
|
6847
6960
|
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(Int(min(timeoutMs + 100, Double(Int.max)))), execute: timeoutWorkItem)
|
|
6848
6961
|
manager.requestLocation()
|
|
6962
|
+
|
|
6963
|
+
#else
|
|
6964
|
+
|
|
6965
|
+
#endif
|
|
6849
6966
|
}
|
|
6850
6967
|
|
|
6851
6968
|
private func finishSingleLocationRequest() {
|
|
@@ -6871,12 +6988,17 @@ struct CraftWebView: UIViewRepresentable {
|
|
|
6871
6988
|
}
|
|
6872
6989
|
|
|
6873
6990
|
private func watchPosition(callbackId: String?) {
|
|
6991
|
+
#if CRAFT_PERMISSION_LOCATION
|
|
6874
6992
|
locationManager?.delegate = self
|
|
6875
6993
|
isWatchingLocation = true
|
|
6876
6994
|
requestLocationAuthorization()
|
|
6877
6995
|
configureBackgroundLocationIfNeeded()
|
|
6878
6996
|
locationManager?.startUpdatingLocation()
|
|
6879
6997
|
resolveCallback(callbackId, result: true)
|
|
6998
|
+
|
|
6999
|
+
#else
|
|
7000
|
+
rejectCallback(callbackId, error: "Capability was not enabled when this app was built", code: "CAPABILITY_DISABLED")
|
|
7001
|
+
#endif
|
|
6880
7002
|
}
|
|
6881
7003
|
|
|
6882
7004
|
private func stopWatchingPosition() {
|
|
@@ -6898,16 +7020,26 @@ struct CraftWebView: UIViewRepresentable {
|
|
|
6898
7020
|
}
|
|
6899
7021
|
|
|
6900
7022
|
private func requestLocationAuthorization() {
|
|
7023
|
+
#if CRAFT_PERMISSION_LOCATION
|
|
6901
7024
|
if config.enableBackgroundLocation {
|
|
6902
7025
|
locationManager?.requestAlwaysAuthorization()
|
|
6903
7026
|
} else {
|
|
6904
7027
|
locationManager?.requestWhenInUseAuthorization()
|
|
6905
7028
|
}
|
|
7029
|
+
|
|
7030
|
+
#else
|
|
7031
|
+
|
|
7032
|
+
#endif
|
|
6906
7033
|
}
|
|
6907
7034
|
|
|
6908
7035
|
private func configureBackgroundLocationIfNeeded() {
|
|
7036
|
+
#if CRAFT_PERMISSION_LOCATION
|
|
6909
7037
|
locationManager?.allowsBackgroundLocationUpdates = config.enableBackgroundLocation
|
|
6910
7038
|
locationManager?.showsBackgroundLocationIndicator = config.enableBackgroundLocation
|
|
7039
|
+
|
|
7040
|
+
#else
|
|
7041
|
+
|
|
7042
|
+
#endif
|
|
6911
7043
|
}
|
|
6912
7044
|
|
|
6913
7045
|
private func persistLocationRecordingState() throws {
|
|
@@ -6916,6 +7048,7 @@ struct CraftWebView: UIViewRepresentable {
|
|
|
6916
7048
|
}
|
|
6917
7049
|
|
|
6918
7050
|
private func restoreLocationRecordingState() throws {
|
|
7051
|
+
#if CRAFT_PERMISSION_LOCATION
|
|
6919
7052
|
guard FileManager.default.fileExists(atPath: locationRecordingStateURL.path) else {
|
|
6920
7053
|
if FileManager.default.fileExists(atPath: locationRecordingTrackURL.path), !(try Data(contentsOf: locationRecordingTrackURL)).isEmpty {
|
|
6921
7054
|
throw CraftNativeActionError(code: "RECORDING_STORAGE_ERROR", message: "Recording metadata is missing")
|
|
@@ -6945,6 +7078,10 @@ struct CraftWebView: UIViewRepresentable {
|
|
|
6945
7078
|
configureBackgroundLocationIfNeeded()
|
|
6946
7079
|
locationManager?.startUpdatingLocation()
|
|
6947
7080
|
}
|
|
7081
|
+
|
|
7082
|
+
#else
|
|
7083
|
+
|
|
7084
|
+
#endif
|
|
6948
7085
|
}
|
|
6949
7086
|
|
|
6950
7087
|
/// Same serial main-queue dispatch as the operation it protects.
|
|
@@ -7016,6 +7153,7 @@ struct CraftWebView: UIViewRepresentable {
|
|
|
7016
7153
|
}
|
|
7017
7154
|
|
|
7018
7155
|
private func startLocationRecording(body: [String: Any], callbackId: String?) {
|
|
7156
|
+
#if CRAFT_PERMISSION_LOCATION
|
|
7019
7157
|
guard !isRecordingLocation else {
|
|
7020
7158
|
rejectCallback(callbackId, error: "A location recording is already active", code: "RECORDING_ALREADY_ACTIVE")
|
|
7021
7159
|
return
|
|
@@ -7051,6 +7189,10 @@ struct CraftWebView: UIViewRepresentable {
|
|
|
7051
7189
|
configureBackgroundLocationIfNeeded()
|
|
7052
7190
|
locationManager?.startUpdatingLocation()
|
|
7053
7191
|
resolveCallback(callbackId, result: locationRecordingSummary())
|
|
7192
|
+
|
|
7193
|
+
#else
|
|
7194
|
+
rejectCallback(callbackId, error: "Capability was not enabled when this app was built", code: "CAPABILITY_DISABLED")
|
|
7195
|
+
#endif
|
|
7054
7196
|
}
|
|
7055
7197
|
|
|
7056
7198
|
private func pauseLocationRecording(callbackId: String?) {
|
|
@@ -7071,6 +7213,7 @@ struct CraftWebView: UIViewRepresentable {
|
|
|
7071
7213
|
}
|
|
7072
7214
|
|
|
7073
7215
|
private func resumeLocationRecording(callbackId: String?) {
|
|
7216
|
+
#if CRAFT_PERMISSION_LOCATION
|
|
7074
7217
|
guard isRecordingLocation else {
|
|
7075
7218
|
rejectCallback(callbackId, error: "No active location recording", code: "NO_ACTIVE_RECORDING")
|
|
7076
7219
|
return
|
|
@@ -7091,6 +7234,10 @@ struct CraftWebView: UIViewRepresentable {
|
|
|
7091
7234
|
configureBackgroundLocationIfNeeded()
|
|
7092
7235
|
locationManager?.startUpdatingLocation()
|
|
7093
7236
|
resolveCallback(callbackId, result: locationRecordingSummary())
|
|
7237
|
+
|
|
7238
|
+
#else
|
|
7239
|
+
rejectCallback(callbackId, error: "Capability was not enabled when this app was built", code: "CAPABILITY_DISABLED")
|
|
7240
|
+
#endif
|
|
7094
7241
|
}
|
|
7095
7242
|
|
|
7096
7243
|
private func stopLocationRecording(callbackId: String?) {
|
|
@@ -7279,6 +7426,7 @@ struct CraftWebView: UIViewRepresentable {
|
|
|
7279
7426
|
|
|
7280
7427
|
// MARK: - Flashlight
|
|
7281
7428
|
private func setFlashlight(enabled: Bool, callbackId: String?) {
|
|
7429
|
+
#if CRAFT_PERMISSION_CAMERA
|
|
7282
7430
|
guard let device = AVCaptureDevice.default(for: .video), device.hasTorch else {
|
|
7283
7431
|
rejectCallback(callbackId, error: "Flashlight not available")
|
|
7284
7432
|
return
|
|
@@ -7292,6 +7440,10 @@ struct CraftWebView: UIViewRepresentable {
|
|
|
7292
7440
|
} catch {
|
|
7293
7441
|
rejectCallback(callbackId, error: error.localizedDescription)
|
|
7294
7442
|
}
|
|
7443
|
+
|
|
7444
|
+
#else
|
|
7445
|
+
rejectCallback(callbackId, error: "Capability was not enabled when this app was built", code: "CAPABILITY_DISABLED")
|
|
7446
|
+
#endif
|
|
7295
7447
|
}
|
|
7296
7448
|
|
|
7297
7449
|
// MARK: - Vibration Pattern
|
|
@@ -7309,6 +7461,7 @@ struct CraftWebView: UIViewRepresentable {
|
|
|
7309
7461
|
|
|
7310
7462
|
// MARK: - Contacts
|
|
7311
7463
|
private func getContacts(callbackId: String?) {
|
|
7464
|
+
#if CRAFT_PERMISSION_CONTACTS
|
|
7312
7465
|
guard let store = contactStore else {
|
|
7313
7466
|
rejectCallback(callbackId, error: "Contacts access is disabled", code: "CAPABILITY_DISABLED")
|
|
7314
7467
|
return
|
|
@@ -7347,9 +7500,14 @@ struct CraftWebView: UIViewRepresentable {
|
|
|
7347
7500
|
self?.rejectCallback(callbackId, error: error.localizedDescription)
|
|
7348
7501
|
}
|
|
7349
7502
|
}
|
|
7503
|
+
|
|
7504
|
+
#else
|
|
7505
|
+
rejectCallback(callbackId, error: "Capability was not enabled when this app was built", code: "CAPABILITY_DISABLED")
|
|
7506
|
+
#endif
|
|
7350
7507
|
}
|
|
7351
7508
|
|
|
7352
7509
|
private func addContact(_ data: [String: Any], callbackId: String?) {
|
|
7510
|
+
#if CRAFT_PERMISSION_CONTACTS
|
|
7353
7511
|
guard let store = contactStore else {
|
|
7354
7512
|
rejectCallback(callbackId, error: "Contacts access is disabled", code: "CAPABILITY_DISABLED")
|
|
7355
7513
|
return
|
|
@@ -7380,10 +7538,15 @@ struct CraftWebView: UIViewRepresentable {
|
|
|
7380
7538
|
self?.rejectCallback(callbackId, error: error.localizedDescription)
|
|
7381
7539
|
}
|
|
7382
7540
|
}
|
|
7541
|
+
|
|
7542
|
+
#else
|
|
7543
|
+
rejectCallback(callbackId, error: "Capability was not enabled when this app was built", code: "CAPABILITY_DISABLED")
|
|
7544
|
+
#endif
|
|
7383
7545
|
}
|
|
7384
7546
|
|
|
7385
7547
|
// MARK: - Calendar
|
|
7386
7548
|
private func getCalendarEvents(startDate: Double?, endDate: Double?, callbackId: String?) {
|
|
7549
|
+
#if CRAFT_PERMISSION_CALENDAR
|
|
7387
7550
|
guard let store = eventStore else {
|
|
7388
7551
|
rejectCallback(callbackId, error: "Calendar access is disabled", code: "CAPABILITY_DISABLED")
|
|
7389
7552
|
return
|
|
@@ -7416,9 +7579,14 @@ struct CraftWebView: UIViewRepresentable {
|
|
|
7416
7579
|
|
|
7417
7580
|
self?.resolveCallback(callbackId, result: eventData)
|
|
7418
7581
|
}
|
|
7582
|
+
|
|
7583
|
+
#else
|
|
7584
|
+
rejectCallback(callbackId, error: "Capability was not enabled when this app was built", code: "CAPABILITY_DISABLED")
|
|
7585
|
+
#endif
|
|
7419
7586
|
}
|
|
7420
7587
|
|
|
7421
7588
|
private func createCalendarEvent(_ data: [String: Any], callbackId: String?) {
|
|
7589
|
+
#if CRAFT_PERMISSION_CALENDAR
|
|
7422
7590
|
guard let store = eventStore else {
|
|
7423
7591
|
rejectCallback(callbackId, error: "Calendar access is disabled", code: "CAPABILITY_DISABLED")
|
|
7424
7592
|
return
|
|
@@ -7454,9 +7622,14 @@ struct CraftWebView: UIViewRepresentable {
|
|
|
7454
7622
|
self?.rejectCallback(callbackId, error: error.localizedDescription)
|
|
7455
7623
|
}
|
|
7456
7624
|
}
|
|
7625
|
+
|
|
7626
|
+
#else
|
|
7627
|
+
rejectCallback(callbackId, error: "Capability was not enabled when this app was built", code: "CAPABILITY_DISABLED")
|
|
7628
|
+
#endif
|
|
7457
7629
|
}
|
|
7458
7630
|
|
|
7459
7631
|
private func deleteCalendarEvent(_ eventId: String, callbackId: String?) {
|
|
7632
|
+
#if CRAFT_PERMISSION_CALENDAR
|
|
7460
7633
|
guard let store = eventStore else {
|
|
7461
7634
|
rejectCallback(callbackId, error: "Calendar access is disabled", code: "CAPABILITY_DISABLED")
|
|
7462
7635
|
return
|
|
@@ -7472,6 +7645,10 @@ struct CraftWebView: UIViewRepresentable {
|
|
|
7472
7645
|
} catch {
|
|
7473
7646
|
rejectCallback(callbackId, error: error.localizedDescription)
|
|
7474
7647
|
}
|
|
7648
|
+
|
|
7649
|
+
#else
|
|
7650
|
+
rejectCallback(callbackId, error: "Capability was not enabled when this app was built", code: "CAPABILITY_DISABLED")
|
|
7651
|
+
#endif
|
|
7475
7652
|
}
|
|
7476
7653
|
|
|
7477
7654
|
// MARK: - Local Notifications
|
|
@@ -7870,6 +8047,7 @@ struct CraftWebView: UIViewRepresentable {
|
|
|
7870
8047
|
|
|
7871
8048
|
// MARK: - QR/Barcode Scanner
|
|
7872
8049
|
private func scanQRCode(callbackId: String?) {
|
|
8050
|
+
#if CRAFT_PERMISSION_CAMERA
|
|
7873
8051
|
DispatchQueue.main.async {
|
|
7874
8052
|
guard let rootVC = CraftPresenter.topViewController() else { return }
|
|
7875
8053
|
|
|
@@ -7888,6 +8066,10 @@ struct CraftWebView: UIViewRepresentable {
|
|
|
7888
8066
|
self.rejectCallback(callbackId, error: "QR scanning requires iOS 16+")
|
|
7889
8067
|
}
|
|
7890
8068
|
}
|
|
8069
|
+
|
|
8070
|
+
#else
|
|
8071
|
+
rejectCallback(callbackId, error: "Capability was not enabled when this app was built", code: "CAPABILITY_DISABLED")
|
|
8072
|
+
#endif
|
|
7891
8073
|
}
|
|
7892
8074
|
|
|
7893
8075
|
// MARK: - File Picker
|
|
@@ -7978,6 +8160,7 @@ struct CraftWebView: UIViewRepresentable {
|
|
|
7978
8160
|
|
|
7979
8161
|
// MARK: - Audio Recording
|
|
7980
8162
|
private func startAudioRecording(callbackId: String?) {
|
|
8163
|
+
#if CRAFT_PERMISSION_MICROPHONE
|
|
7981
8164
|
AVAudioSession.sharedInstance().requestRecordPermission { [weak self] granted in
|
|
7982
8165
|
guard granted else {
|
|
7983
8166
|
self?.rejectCallback(callbackId, error: "Microphone permission denied")
|
|
@@ -8008,9 +8191,14 @@ struct CraftWebView: UIViewRepresentable {
|
|
|
8008
8191
|
}
|
|
8009
8192
|
}
|
|
8010
8193
|
}
|
|
8194
|
+
|
|
8195
|
+
#else
|
|
8196
|
+
rejectCallback(callbackId, error: "Capability was not enabled when this app was built", code: "CAPABILITY_DISABLED")
|
|
8197
|
+
#endif
|
|
8011
8198
|
}
|
|
8012
8199
|
|
|
8013
8200
|
private func stopAudioRecording(callbackId: String?) {
|
|
8201
|
+
#if CRAFT_PERMISSION_MICROPHONE
|
|
8014
8202
|
audioRecorder?.stop()
|
|
8015
8203
|
audioRecorder = nil
|
|
8016
8204
|
|
|
@@ -8025,10 +8213,15 @@ struct CraftWebView: UIViewRepresentable {
|
|
|
8025
8213
|
rejectCallback(callbackId, error: "No recording found")
|
|
8026
8214
|
}
|
|
8027
8215
|
recordingURL = nil
|
|
8216
|
+
|
|
8217
|
+
#else
|
|
8218
|
+
rejectCallback(callbackId, error: "Capability was not enabled when this app was built", code: "CAPABILITY_DISABLED")
|
|
8219
|
+
#endif
|
|
8028
8220
|
}
|
|
8029
8221
|
|
|
8030
8222
|
// MARK: - Video Recording
|
|
8031
8223
|
private func startVideoRecording(callbackId: String?) {
|
|
8224
|
+
#if CRAFT_PERMISSION_CAMERA
|
|
8032
8225
|
DispatchQueue.main.async {
|
|
8033
8226
|
guard UIImagePickerController.isSourceTypeAvailable(.camera) else {
|
|
8034
8227
|
self.rejectCallback(callbackId, error: "Camera not available")
|
|
@@ -8045,10 +8238,15 @@ struct CraftWebView: UIViewRepresentable {
|
|
|
8045
8238
|
self.pendingCallbackId = callbackId
|
|
8046
8239
|
rootVC.present(picker, animated: true)
|
|
8047
8240
|
}
|
|
8241
|
+
|
|
8242
|
+
#else
|
|
8243
|
+
rejectCallback(callbackId, error: "Capability was not enabled when this app was built", code: "CAPABILITY_DISABLED")
|
|
8244
|
+
#endif
|
|
8048
8245
|
}
|
|
8049
8246
|
|
|
8050
8247
|
// MARK: - Motion Sensors
|
|
8051
8248
|
private func startMotionUpdates(interval: Double, callbackId: String?) {
|
|
8249
|
+
#if CRAFT_PERMISSION_MOTION
|
|
8052
8250
|
guard let motionManager = motionManager, motionManager.isDeviceMotionAvailable else {
|
|
8053
8251
|
rejectCallback(callbackId, error: "Motion sensors not available")
|
|
8054
8252
|
return
|
|
@@ -8081,11 +8279,20 @@ struct CraftWebView: UIViewRepresentable {
|
|
|
8081
8279
|
|
|
8082
8280
|
isMotionUpdating = true
|
|
8083
8281
|
resolveCallback(callbackId, result: true)
|
|
8282
|
+
|
|
8283
|
+
#else
|
|
8284
|
+
rejectCallback(callbackId, error: "Capability was not enabled when this app was built", code: "CAPABILITY_DISABLED")
|
|
8285
|
+
#endif
|
|
8084
8286
|
}
|
|
8085
8287
|
|
|
8086
8288
|
private func stopMotionUpdates() {
|
|
8289
|
+
#if CRAFT_PERMISSION_MOTION
|
|
8087
8290
|
motionManager?.stopDeviceMotionUpdates()
|
|
8088
8291
|
isMotionUpdating = false
|
|
8292
|
+
|
|
8293
|
+
#else
|
|
8294
|
+
|
|
8295
|
+
#endif
|
|
8089
8296
|
}
|
|
8090
8297
|
|
|
8091
8298
|
// MARK: - Local Database (SQLite)
|
|
@@ -8197,19 +8404,30 @@ struct CraftWebView: UIViewRepresentable {
|
|
|
8197
8404
|
|
|
8198
8405
|
// MARK: - Bluetooth
|
|
8199
8406
|
private func startBluetoothScan(callbackId: String?) {
|
|
8407
|
+
#if CRAFT_PERMISSION_BLUETOOTH
|
|
8200
8408
|
centralManager = CBCentralManager(delegate: self, queue: nil)
|
|
8201
8409
|
pendingCallbackId = callbackId
|
|
8202
8410
|
// Scanning starts in centralManagerDidUpdateState
|
|
8411
|
+
|
|
8412
|
+
#else
|
|
8413
|
+
rejectCallback(callbackId, error: "Capability was not enabled when this app was built", code: "CAPABILITY_DISABLED")
|
|
8414
|
+
#endif
|
|
8203
8415
|
}
|
|
8204
8416
|
|
|
8205
8417
|
private func stopBluetoothScan() {
|
|
8418
|
+
#if CRAFT_PERMISSION_BLUETOOTH
|
|
8206
8419
|
centralManager?.stopScan()
|
|
8207
8420
|
centralManager = nil
|
|
8208
8421
|
discoveredPeripherals.removeAll()
|
|
8422
|
+
|
|
8423
|
+
#else
|
|
8424
|
+
|
|
8425
|
+
#endif
|
|
8209
8426
|
}
|
|
8210
8427
|
|
|
8211
8428
|
// MARK: - NFC
|
|
8212
8429
|
private func scanNFC(callbackId: String?) {
|
|
8430
|
+
#if CRAFT_PERMISSION_NFC
|
|
8213
8431
|
guard NFCNDEFReaderSession.readingAvailable else {
|
|
8214
8432
|
rejectCallback(callbackId, error: "NFC not available")
|
|
8215
8433
|
return
|
|
@@ -8219,6 +8437,10 @@ struct CraftWebView: UIViewRepresentable {
|
|
|
8219
8437
|
let session = NFCNDEFReaderSession(delegate: self, queue: nil, invalidateAfterFirstRead: true)
|
|
8220
8438
|
session.alertMessage = "Hold your iPhone near the NFC tag"
|
|
8221
8439
|
session.begin()
|
|
8440
|
+
|
|
8441
|
+
#else
|
|
8442
|
+
rejectCallback(callbackId, error: "NFC was not enabled when this app was built", code: "CAPABILITY_DISABLED")
|
|
8443
|
+
#endif
|
|
8222
8444
|
}
|
|
8223
8445
|
|
|
8224
8446
|
// MARK: - Health
|
|
@@ -8442,6 +8664,7 @@ struct CraftWebView: UIViewRepresentable {
|
|
|
8442
8664
|
|
|
8443
8665
|
// MARK: - Contacts Picker
|
|
8444
8666
|
private func pickContact(multiple: Bool, callbackId: String?) {
|
|
8667
|
+
#if CRAFT_PERMISSION_CONTACTS
|
|
8445
8668
|
DispatchQueue.main.async {
|
|
8446
8669
|
guard let rootVC = CraftPresenter.topViewController() else {
|
|
8447
8670
|
self.rejectCallback(callbackId, error: "No root view controller")
|
|
@@ -8462,6 +8685,10 @@ struct CraftWebView: UIViewRepresentable {
|
|
|
8462
8685
|
|
|
8463
8686
|
rootVC.present(picker, animated: true)
|
|
8464
8687
|
}
|
|
8688
|
+
|
|
8689
|
+
#else
|
|
8690
|
+
rejectCallback(callbackId, error: "Capability was not enabled when this app was built", code: "CAPABILITY_DISABLED")
|
|
8691
|
+
#endif
|
|
8465
8692
|
}
|
|
8466
8693
|
|
|
8467
8694
|
// MARK: - App Shortcuts
|
|
@@ -8611,6 +8838,7 @@ struct CraftWebView: UIViewRepresentable {
|
|
|
8611
8838
|
private var detectedPlanes: [UUID: ARPlaneAnchor] = [:]
|
|
8612
8839
|
|
|
8613
8840
|
private func startAR(options: [String: Any], callbackId: String?) {
|
|
8841
|
+
#if CRAFT_PERMISSION_CAMERA
|
|
8614
8842
|
guard ARWorldTrackingConfiguration.isSupported else {
|
|
8615
8843
|
rejectCallback(callbackId, error: "AR not supported on this device")
|
|
8616
8844
|
return
|
|
@@ -8642,9 +8870,14 @@ struct CraftWebView: UIViewRepresentable {
|
|
|
8642
8870
|
arView.session.run(configuration)
|
|
8643
8871
|
self.resolveCallback(callbackId, result: ["started": true])
|
|
8644
8872
|
}
|
|
8873
|
+
|
|
8874
|
+
#else
|
|
8875
|
+
rejectCallback(callbackId, error: "Capability was not enabled when this app was built", code: "CAPABILITY_DISABLED")
|
|
8876
|
+
#endif
|
|
8645
8877
|
}
|
|
8646
8878
|
|
|
8647
8879
|
private func stopAR(callbackId: String?) {
|
|
8880
|
+
#if CRAFT_PERMISSION_CAMERA
|
|
8648
8881
|
DispatchQueue.main.async { [weak self] in
|
|
8649
8882
|
guard let self = self else { return }
|
|
8650
8883
|
|
|
@@ -8657,9 +8890,14 @@ struct CraftWebView: UIViewRepresentable {
|
|
|
8657
8890
|
|
|
8658
8891
|
self.resolveCallback(callbackId, result: ["stopped": true])
|
|
8659
8892
|
}
|
|
8893
|
+
|
|
8894
|
+
#else
|
|
8895
|
+
rejectCallback(callbackId, error: "Capability was not enabled when this app was built", code: "CAPABILITY_DISABLED")
|
|
8896
|
+
#endif
|
|
8660
8897
|
}
|
|
8661
8898
|
|
|
8662
8899
|
private func placeARObject(model: String, position: [String: Double]?, callbackId: String?) {
|
|
8900
|
+
#if CRAFT_PERMISSION_CAMERA
|
|
8663
8901
|
DispatchQueue.main.async { [weak self] in
|
|
8664
8902
|
guard let self = self, let arView = self.arView else {
|
|
8665
8903
|
self?.rejectCallback(callbackId, error: "AR not started")
|
|
@@ -8723,9 +8961,14 @@ struct CraftWebView: UIViewRepresentable {
|
|
|
8723
8961
|
|
|
8724
8962
|
self.resolveCallback(callbackId, result: ["objectId": objectId, "placed": true])
|
|
8725
8963
|
}
|
|
8964
|
+
|
|
8965
|
+
#else
|
|
8966
|
+
rejectCallback(callbackId, error: "Capability was not enabled when this app was built", code: "CAPABILITY_DISABLED")
|
|
8967
|
+
#endif
|
|
8726
8968
|
}
|
|
8727
8969
|
|
|
8728
8970
|
private func removeARObject(objectId: String, callbackId: String?) {
|
|
8971
|
+
#if CRAFT_PERMISSION_CAMERA
|
|
8729
8972
|
DispatchQueue.main.async { [weak self] in
|
|
8730
8973
|
guard let self = self else { return }
|
|
8731
8974
|
|
|
@@ -8737,9 +8980,14 @@ struct CraftWebView: UIViewRepresentable {
|
|
|
8737
8980
|
self.rejectCallback(callbackId, error: "Object not found")
|
|
8738
8981
|
}
|
|
8739
8982
|
}
|
|
8983
|
+
|
|
8984
|
+
#else
|
|
8985
|
+
rejectCallback(callbackId, error: "Capability was not enabled when this app was built", code: "CAPABILITY_DISABLED")
|
|
8986
|
+
#endif
|
|
8740
8987
|
}
|
|
8741
8988
|
|
|
8742
8989
|
private func getARPlanes(callbackId: String?) {
|
|
8990
|
+
#if CRAFT_PERMISSION_CAMERA
|
|
8743
8991
|
var planes: [[String: Any]] = []
|
|
8744
8992
|
for (_, anchor) in detectedPlanes {
|
|
8745
8993
|
planes.append([
|
|
@@ -8750,6 +8998,10 @@ struct CraftWebView: UIViewRepresentable {
|
|
|
8750
8998
|
])
|
|
8751
8999
|
}
|
|
8752
9000
|
resolveCallback(callbackId, result: planes)
|
|
9001
|
+
|
|
9002
|
+
#else
|
|
9003
|
+
rejectCallback(callbackId, error: "Capability was not enabled when this app was built", code: "CAPABILITY_DISABLED")
|
|
9004
|
+
#endif
|
|
8753
9005
|
}
|
|
8754
9006
|
|
|
8755
9007
|
// MARK: - ML (Core ML / Vision)
|
|
@@ -9142,6 +9394,7 @@ extension CraftWebView.Coordinator: UIDocumentPickerDelegate {
|
|
|
9142
9394
|
}
|
|
9143
9395
|
|
|
9144
9396
|
// MARK: - Bluetooth Delegate
|
|
9397
|
+
#if CRAFT_PERMISSION_BLUETOOTH
|
|
9145
9398
|
extension CraftWebView.Coordinator: CBCentralManagerDelegate {
|
|
9146
9399
|
func centralManagerDidUpdateState(_ central: CBCentralManager) {
|
|
9147
9400
|
if central.state == .poweredOn {
|
|
@@ -9164,8 +9417,10 @@ extension CraftWebView.Coordinator: CBCentralManagerDelegate {
|
|
|
9164
9417
|
}
|
|
9165
9418
|
}
|
|
9166
9419
|
}
|
|
9420
|
+
#endif
|
|
9167
9421
|
|
|
9168
9422
|
// MARK: - NFC Delegate
|
|
9423
|
+
#if CRAFT_PERMISSION_NFC
|
|
9169
9424
|
extension CraftWebView.Coordinator: NFCNDEFReaderSessionDelegate {
|
|
9170
9425
|
func readerSession(_ session: NFCNDEFReaderSession, didDetectNDEFs messages: [NFCNDEFMessage]) {
|
|
9171
9426
|
var records: [[String: Any]] = []
|
|
@@ -9195,6 +9450,7 @@ extension CraftWebView.Coordinator: NFCNDEFReaderSessionDelegate {
|
|
|
9195
9450
|
pendingCallbackId = nil
|
|
9196
9451
|
}
|
|
9197
9452
|
}
|
|
9453
|
+
#endif
|
|
9198
9454
|
|
|
9199
9455
|
// MARK: - ARSCNViewDelegate Extension
|
|
9200
9456
|
extension CraftWebView.Coordinator {
|
|
@@ -9333,6 +9589,24 @@ extension CraftWebView.Coordinator: WKUIDelegate {
|
|
|
9333
9589
|
/// thing reads as a web page. Anything embedded still gets the prompt.
|
|
9334
9590
|
@available(iOS 15.0, *)
|
|
9335
9591
|
func webView(_ webView: WKWebView, requestMediaCapturePermissionFor origin: WKSecurityOrigin, initiatedByFrame frame: WKFrameInfo, type: WKMediaCaptureType, decisionHandler: @escaping (WKPermissionDecision) -> Void) {
|
|
9592
|
+
#if CRAFT_PERMISSION_CAMERA
|
|
9593
|
+
let cameraEnabled = true
|
|
9594
|
+
#else
|
|
9595
|
+
let cameraEnabled = false
|
|
9596
|
+
#endif
|
|
9597
|
+
#if CRAFT_PERMISSION_MICROPHONE
|
|
9598
|
+
let microphoneEnabled = true
|
|
9599
|
+
#else
|
|
9600
|
+
let microphoneEnabled = false
|
|
9601
|
+
#endif
|
|
9602
|
+
let enabled: Bool
|
|
9603
|
+
switch type {
|
|
9604
|
+
case .camera: enabled = cameraEnabled
|
|
9605
|
+
case .microphone: enabled = microphoneEnabled
|
|
9606
|
+
case .cameraAndMicrophone: enabled = cameraEnabled && microphoneEnabled
|
|
9607
|
+
@unknown default: enabled = false
|
|
9608
|
+
}
|
|
9609
|
+
guard enabled else { decisionHandler(.deny); return }
|
|
9336
9610
|
decisionHandler(trusts(origin) && trusts(frame.securityOrigin) ? .grant : .prompt)
|
|
9337
9611
|
}
|
|
9338
9612
|
}
|
|
@@ -9347,6 +9621,7 @@ extension CraftWebView.Coordinator: AVSpeechSynthesizerDelegate {
|
|
|
9347
9621
|
}
|
|
9348
9622
|
}
|
|
9349
9623
|
|
|
9624
|
+
#if CRAFT_PERMISSION_CONTACTS
|
|
9350
9625
|
extension CraftWebView.Coordinator: CNContactPickerDelegate {
|
|
9351
9626
|
func contactPicker(_ picker: CNContactPickerViewController, didSelect contact: CNContact) {
|
|
9352
9627
|
let contactData = formatContact(contact)
|
|
@@ -9396,6 +9671,7 @@ extension CraftWebView.Coordinator: CNContactPickerDelegate {
|
|
|
9396
9671
|
return data
|
|
9397
9672
|
}
|
|
9398
9673
|
}
|
|
9674
|
+
#endif
|
|
9399
9675
|
|
|
9400
9676
|
// MARK: - Watch Connectivity Delegate
|
|
9401
9677
|
extension CraftWebView.Coordinator: WCSessionDelegate {
|