@thelacanians/vue-native-cli 0.4.2 → 0.4.4

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 (172) hide show
  1. package/dist/cli.js +43 -23
  2. package/native/android/README.md +205 -0
  3. package/native/android/VueNativeCore/build.gradle.kts +100 -0
  4. package/native/android/VueNativeCore/consumer-rules.pro +12 -0
  5. package/native/android/VueNativeCore/proguard-rules.pro +33 -0
  6. package/native/android/VueNativeCore/src/main/AndroidManifest.xml +17 -0
  7. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Bridge/ErrorOverlayView.kt +94 -0
  8. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Bridge/HotReloadManager.kt +105 -0
  9. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Bridge/JSPolyfills.kt +652 -0
  10. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Bridge/JSRuntime.kt +207 -0
  11. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Bridge/NativeBridge.kt +417 -0
  12. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/ComponentRegistry.kt +76 -0
  13. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VActionSheetFactory.kt +78 -0
  14. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VActivityIndicatorFactory.kt +46 -0
  15. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VAlertDialogFactory.kt +84 -0
  16. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VButtonFactory.kt +73 -0
  17. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VCheckboxFactory.kt +93 -0
  18. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VDropdownFactory.kt +125 -0
  19. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VImageFactory.kt +75 -0
  20. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VInputFactory.kt +210 -0
  21. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VKeyboardAvoidingFactory.kt +31 -0
  22. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VListFactory.kt +183 -0
  23. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VModalFactory.kt +105 -0
  24. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VPickerFactory.kt +57 -0
  25. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VPressableFactory.kt +109 -0
  26. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VProgressBarFactory.kt +43 -0
  27. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VRadioFactory.kt +103 -0
  28. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VRefreshControlFactory.kt +73 -0
  29. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VRootFactory.kt +39 -0
  30. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VSafeAreaFactory.kt +48 -0
  31. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VScrollViewFactory.kt +105 -0
  32. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VSectionListFactory.kt +144 -0
  33. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VSegmentedControlFactory.kt +77 -0
  34. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VSliderFactory.kt +74 -0
  35. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VStatusBarFactory.kt +52 -0
  36. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VSwitchFactory.kt +62 -0
  37. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VTextFactory.kt +53 -0
  38. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VVideoFactory.kt +191 -0
  39. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VViewFactory.kt +48 -0
  40. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VWebViewFactory.kt +90 -0
  41. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/NativeComponentFactory.kt +40 -0
  42. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/VTextNodeView.kt +23 -0
  43. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Helpers/GestureHelper.kt +16 -0
  44. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Helpers/TouchableView.kt +105 -0
  45. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/AnimationModule.kt +292 -0
  46. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/AppStateModule.kt +41 -0
  47. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/AsyncStorageModule.kt +59 -0
  48. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/AudioModule.kt +331 -0
  49. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/BackgroundTaskModule.kt +166 -0
  50. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/BiometryModule.kt +56 -0
  51. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/BluetoothModule.kt +302 -0
  52. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/CalendarModule.kt +198 -0
  53. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/CameraModule.kt +64 -0
  54. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/ClipboardModule.kt +36 -0
  55. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/ContactsModule.kt +288 -0
  56. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/DatabaseModule.kt +229 -0
  57. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/DeviceInfoModule.kt +39 -0
  58. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/FileSystemModule.kt +193 -0
  59. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/GeolocationModule.kt +68 -0
  60. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/HapticsModule.kt +61 -0
  61. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/HttpModule.kt +111 -0
  62. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/IAPModule.kt +302 -0
  63. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/KeyboardModule.kt +26 -0
  64. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/LinkingModule.kt +43 -0
  65. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/NativeModule.kt +27 -0
  66. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/NativeModuleRegistry.kt +92 -0
  67. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/NetworkModule.kt +75 -0
  68. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/NotificationsModule.kt +181 -0
  69. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/OTAModule.kt +255 -0
  70. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/PerformanceModule.kt +147 -0
  71. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/PermissionsModule.kt +126 -0
  72. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/SecureStorageModule.kt +51 -0
  73. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/SensorsModule.kt +134 -0
  74. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/ShareModule.kt +36 -0
  75. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/SocialAuthModule.kt +160 -0
  76. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/WebSocketModule.kt +155 -0
  77. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Styling/StyleEngine.kt +802 -0
  78. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Tags.kt +43 -0
  79. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/VueNativeActivity.kt +169 -0
  80. package/native/android/VueNativeCore/src/main/res/values/ids.xml +8 -0
  81. package/native/android/app/build.gradle.kts +45 -0
  82. package/native/android/app/proguard-rules.pro +5 -0
  83. package/native/android/app/src/main/AndroidManifest.xml +25 -0
  84. package/native/android/app/src/main/assets/.gitkeep +0 -0
  85. package/native/android/app/src/main/kotlin/com/vuenative/example/counter/MainActivity.kt +14 -0
  86. package/native/android/app/src/main/res/layout/activity_main.xml +6 -0
  87. package/native/android/app/src/main/res/values/strings.xml +3 -0
  88. package/native/android/app/src/main/res/values/themes.xml +9 -0
  89. package/native/android/app/src/main/res/xml/network_security_config.xml +8 -0
  90. package/native/android/build.gradle.kts +6 -0
  91. package/native/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  92. package/native/android/gradle/wrapper/gradle-wrapper.properties +7 -0
  93. package/native/android/gradle.properties +4 -0
  94. package/native/android/gradlew +87 -0
  95. package/native/android/gradlew.bat +48 -0
  96. package/native/android/settings.gradle.kts +20 -0
  97. package/native/ios/VueNativeCore/Package.resolved +23 -0
  98. package/native/ios/VueNativeCore/Package.swift +32 -0
  99. package/native/ios/VueNativeCore/Sources/VueNativeCore/Bridge/CertificatePinning.swift +132 -0
  100. package/native/ios/VueNativeCore/Sources/VueNativeCore/Bridge/ErrorOverlayView.swift +92 -0
  101. package/native/ios/VueNativeCore/Sources/VueNativeCore/Bridge/HotReloadManager.swift +147 -0
  102. package/native/ios/VueNativeCore/Sources/VueNativeCore/Bridge/JSPolyfills.swift +711 -0
  103. package/native/ios/VueNativeCore/Sources/VueNativeCore/Bridge/JSRuntime.swift +421 -0
  104. package/native/ios/VueNativeCore/Sources/VueNativeCore/Bridge/NativeBridge.swift +891 -0
  105. package/native/ios/VueNativeCore/Sources/VueNativeCore/Bridge/VueNativeViewController.swift +88 -0
  106. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/ComponentRegistry.swift +193 -0
  107. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VActionSheetFactory.swift +91 -0
  108. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VActivityIndicatorFactory.swift +74 -0
  109. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VAlertDialogFactory.swift +150 -0
  110. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VButtonFactory.swift +93 -0
  111. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VCheckboxFactory.swift +114 -0
  112. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VDropdownFactory.swift +112 -0
  113. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VImageFactory.swift +172 -0
  114. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VInputFactory.swift +357 -0
  115. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VKeyboardAvoidingFactory.swift +99 -0
  116. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VListFactory.swift +250 -0
  117. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VModalFactory.swift +112 -0
  118. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VPickerFactory.swift +96 -0
  119. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VPressableFactory.swift +168 -0
  120. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VProgressBarFactory.swift +39 -0
  121. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VRadioFactory.swift +167 -0
  122. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VRefreshControlFactory.swift +153 -0
  123. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VSafeAreaFactory.swift +56 -0
  124. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VScrollViewFactory.swift +240 -0
  125. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VSectionListFactory.swift +248 -0
  126. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VSegmentedControlFactory.swift +73 -0
  127. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VSliderFactory.swift +63 -0
  128. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VStatusBarFactory.swift +50 -0
  129. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VSwitchFactory.swift +108 -0
  130. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VTextFactory.swift +290 -0
  131. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VVideoFactory.swift +246 -0
  132. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VViewFactory.swift +157 -0
  133. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VWebViewFactory.swift +172 -0
  134. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/NativeComponentFactory.swift +53 -0
  135. package/native/ios/VueNativeCore/Sources/VueNativeCore/Helpers/GestureWrapper.swift +107 -0
  136. package/native/ios/VueNativeCore/Sources/VueNativeCore/Helpers/TouchableView.swift +136 -0
  137. package/native/ios/VueNativeCore/Sources/VueNativeCore/Helpers/UIColor+Hex.swift +80 -0
  138. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/AnimationModule.swift +291 -0
  139. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/AppStateModule.swift +65 -0
  140. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/AsyncStorageModule.swift +68 -0
  141. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/AudioModule.swift +366 -0
  142. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/BackgroundTaskModule.swift +135 -0
  143. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/BiometryModule.swift +61 -0
  144. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/BluetoothModule.swift +387 -0
  145. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/CalendarModule.swift +161 -0
  146. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/CameraModule.swift +318 -0
  147. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/ClipboardModule.swift +33 -0
  148. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/ContactsModule.swift +173 -0
  149. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/DatabaseModule.swift +259 -0
  150. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/DeviceInfoModule.swift +34 -0
  151. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/FileSystemModule.swift +233 -0
  152. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/GeolocationModule.swift +147 -0
  153. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/HapticsModule.swift +50 -0
  154. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/IAPModule.swift +194 -0
  155. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/KeyboardModule.swift +31 -0
  156. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/LinkingModule.swift +42 -0
  157. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/NativeModule.swift +28 -0
  158. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/NativeModuleRegistry.swift +78 -0
  159. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/NetworkModule.swift +62 -0
  160. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/NotificationsModule.swift +215 -0
  161. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/OTAModule.swift +281 -0
  162. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/PerformanceModule.swift +138 -0
  163. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/PermissionsModule.swift +190 -0
  164. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/SecureStorageModule.swift +118 -0
  165. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/SensorsModule.swift +103 -0
  166. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/ShareModule.swift +49 -0
  167. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/SocialAuthModule.swift +240 -0
  168. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/WebSocketModule.swift +213 -0
  169. package/native/ios/VueNativeCore/Sources/VueNativeCore/Resources/vue-native-placeholder.js +8 -0
  170. package/native/ios/VueNativeCore/Sources/VueNativeCore/Styling/StyleEngine.swift +885 -0
  171. package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/JSRuntimeTests.swift +362 -0
  172. package/package.json +3 -2
@@ -0,0 +1,68 @@
1
+ #if canImport(UIKit)
2
+ import Foundation
3
+
4
+ /// Native module providing async key-value storage backed by UserDefaults.
5
+ ///
6
+ /// Methods:
7
+ /// - getItem(key: String) -> String?
8
+ /// - setItem(key: String, value: String)
9
+ /// - removeItem(key: String)
10
+ /// - getAllKeys() -> [String]
11
+ /// - clear()
12
+ final class AsyncStorageModule: NativeModule {
13
+ let moduleName = "AsyncStorage"
14
+
15
+ private let defaults = UserDefaults.standard
16
+ private let keyPrefix = "VueNative.AsyncStorage."
17
+
18
+ func invoke(method: String, args: [Any], callback: @escaping (Any?, String?) -> Void) {
19
+ DispatchQueue.global(qos: .userInitiated).async { [weak self] in
20
+ guard let self = self else { return }
21
+ switch method {
22
+ case "getItem":
23
+ guard let key = args.first as? String else {
24
+ callback(nil, "getItem: missing key")
25
+ return
26
+ }
27
+ let result = self.defaults.string(forKey: self.keyPrefix + key)
28
+ callback(result, nil)
29
+
30
+ case "setItem":
31
+ guard args.count >= 2,
32
+ let key = args[0] as? String,
33
+ let val = args[1] as? String else {
34
+ callback(nil, "setItem: missing key or value")
35
+ return
36
+ }
37
+ self.defaults.set(val, forKey: self.keyPrefix + key)
38
+ callback(nil, nil)
39
+
40
+ case "removeItem":
41
+ guard let key = args.first as? String else {
42
+ callback(nil, "removeItem: missing key")
43
+ return
44
+ }
45
+ self.defaults.removeObject(forKey: self.keyPrefix + key)
46
+ callback(nil, nil)
47
+
48
+ case "getAllKeys":
49
+ let allKeys = self.defaults.dictionaryRepresentation().keys
50
+ .filter { $0.hasPrefix(self.keyPrefix) }
51
+ .map { String($0.dropFirst(self.keyPrefix.count)) }
52
+ callback(allKeys, nil)
53
+
54
+ case "clear":
55
+ let allKeys = self.defaults.dictionaryRepresentation().keys
56
+ .filter { $0.hasPrefix(self.keyPrefix) }
57
+ for key in allKeys {
58
+ self.defaults.removeObject(forKey: key)
59
+ }
60
+ callback(nil, nil)
61
+
62
+ default:
63
+ callback(nil, "AsyncStorageModule: Unknown method '\(method)'")
64
+ }
65
+ }
66
+ }
67
+ }
68
+ #endif
@@ -0,0 +1,366 @@
1
+ #if canImport(UIKit)
2
+ import UIKit
3
+ import AVFoundation
4
+
5
+ /// Native module for audio playback and recording.
6
+ ///
7
+ /// Methods:
8
+ /// - play(uri: String, options?: Object) -- play audio from URI
9
+ /// - pause() -- pause playback
10
+ /// - resume() -- resume playback
11
+ /// - stop() -- stop playback and release player
12
+ /// - seek(position: Number) -- seek to position in seconds
13
+ /// - setVolume(volume: Number) -- set volume 0.0–1.0
14
+ /// - startRecording(options?: Object) -- start audio recording
15
+ /// - stopRecording() -- stop recording, returns { uri, duration }
16
+ /// - pauseRecording() -- pause recording
17
+ /// - resumeRecording() -- resume recording
18
+ /// - getStatus() -- returns current playback status
19
+ ///
20
+ /// Events (via bridge.sendGlobalEvent):
21
+ /// - audio:progress { currentTime, duration }
22
+ /// - audio:complete {}
23
+ /// - audio:error { message }
24
+ final class AudioModule: NativeModule {
25
+ let moduleName = "Audio"
26
+
27
+ private var player: AVAudioPlayer?
28
+ private var recorder: AVAudioRecorder?
29
+ private var displayLink: CADisplayLink?
30
+ private var isPlaying = false
31
+ private var bridge: NativeBridge? { NativeBridge.shared }
32
+
33
+ // MARK: - Delegate to forward completion events
34
+ private var playerDelegate: AudioPlayerDelegate?
35
+
36
+ func invoke(method: String, args: [Any], callback: @escaping (Any?, String?) -> Void) {
37
+ switch method {
38
+ case "play":
39
+ let uri = args.first as? String ?? ""
40
+ let options = (args.count > 1 ? args[1] as? [String: Any] : nil) ?? [:]
41
+ play(uri: uri, options: options, callback: callback)
42
+
43
+ case "pause":
44
+ pause(callback: callback)
45
+
46
+ case "resume":
47
+ resume(callback: callback)
48
+
49
+ case "stop":
50
+ stop(callback: callback)
51
+
52
+ case "seek":
53
+ let position = (args.first as? Double) ?? (args.first as? Int).map(Double.init) ?? 0
54
+ seek(position: position, callback: callback)
55
+
56
+ case "setVolume":
57
+ let volume = (args.first as? Double) ?? (args.first as? Int).map(Double.init) ?? 1.0
58
+ setVolume(Float(volume), callback: callback)
59
+
60
+ case "startRecording":
61
+ let options = args.first as? [String: Any] ?? [:]
62
+ startRecording(options: options, callback: callback)
63
+
64
+ case "stopRecording":
65
+ stopRecording(callback: callback)
66
+
67
+ case "pauseRecording":
68
+ pauseRecording(callback: callback)
69
+
70
+ case "resumeRecording":
71
+ resumeRecording(callback: callback)
72
+
73
+ case "getStatus":
74
+ getStatus(callback: callback)
75
+
76
+ default:
77
+ callback(nil, "AudioModule: Unknown method '\(method)'")
78
+ }
79
+ }
80
+
81
+ // MARK: - Playback
82
+
83
+ private func play(uri: String, options: [String: Any], callback: @escaping (Any?, String?) -> Void) {
84
+ DispatchQueue.main.async { [weak self] in
85
+ guard let self = self else { return }
86
+
87
+ // Stop any existing playback
88
+ self.stopProgressReporting()
89
+ self.player?.stop()
90
+
91
+ do {
92
+ try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default)
93
+ try AVAudioSession.sharedInstance().setActive(true)
94
+ } catch {
95
+ callback(nil, "Failed to set audio session: \(error.localizedDescription)")
96
+ return
97
+ }
98
+
99
+ guard let url = URL(string: uri) else {
100
+ callback(nil, "Invalid audio URI: \(uri)")
101
+ return
102
+ }
103
+
104
+ // Check if it's a remote URL — download first
105
+ if url.scheme == "http" || url.scheme == "https" {
106
+ self.downloadAndPlay(url: url, options: options, callback: callback)
107
+ } else {
108
+ self.playLocal(url: url, options: options, callback: callback)
109
+ }
110
+ }
111
+ }
112
+
113
+ private func downloadAndPlay(url: URL, options: [String: Any], callback: @escaping (Any?, String?) -> Void) {
114
+ URLSession.shared.dataTask(with: url) { [weak self] data, _, error in
115
+ DispatchQueue.main.async {
116
+ guard let self = self else { return }
117
+ if let error = error {
118
+ callback(nil, "Failed to download audio: \(error.localizedDescription)")
119
+ self.bridge?.sendGlobalEvent("audio:error", payload: ["message": error.localizedDescription])
120
+ return
121
+ }
122
+ guard let data = data else {
123
+ callback(nil, "No audio data received")
124
+ return
125
+ }
126
+ do {
127
+ let player = try AVAudioPlayer(data: data)
128
+ self.setupPlayer(player, options: options, callback: callback)
129
+ } catch {
130
+ callback(nil, "Failed to initialize player: \(error.localizedDescription)")
131
+ }
132
+ }
133
+ }.resume()
134
+ }
135
+
136
+ private func playLocal(url: URL, options: [String: Any], callback: @escaping (Any?, String?) -> Void) {
137
+ do {
138
+ let player = try AVAudioPlayer(contentsOf: url)
139
+ setupPlayer(player, options: options, callback: callback)
140
+ } catch {
141
+ callback(nil, "Failed to play audio: \(error.localizedDescription)")
142
+ }
143
+ }
144
+
145
+ private func setupPlayer(_ player: AVAudioPlayer, options: [String: Any], callback: @escaping (Any?, String?) -> Void) {
146
+ let volume = Float(options["volume"] as? Double ?? 1.0)
147
+ let loop = options["loop"] as? Bool ?? false
148
+
149
+ player.volume = volume
150
+ player.numberOfLoops = loop ? -1 : 0
151
+
152
+ let delegate = AudioPlayerDelegate { [weak self] successfully in
153
+ guard let self = self else { return }
154
+ self.isPlaying = false
155
+ self.stopProgressReporting()
156
+ self.bridge?.sendGlobalEvent("audio:complete", payload: [:])
157
+ }
158
+ player.delegate = delegate
159
+ self.playerDelegate = delegate
160
+ self.player = player
161
+
162
+ player.prepareToPlay()
163
+ player.play()
164
+ self.isPlaying = true
165
+ self.startProgressReporting()
166
+
167
+ callback([
168
+ "duration": player.duration,
169
+ "currentTime": 0.0,
170
+ ], nil)
171
+ }
172
+
173
+ private func pause(callback: @escaping (Any?, String?) -> Void) {
174
+ DispatchQueue.main.async { [weak self] in
175
+ self?.player?.pause()
176
+ self?.isPlaying = false
177
+ self?.stopProgressReporting()
178
+ callback(nil, nil)
179
+ }
180
+ }
181
+
182
+ private func resume(callback: @escaping (Any?, String?) -> Void) {
183
+ DispatchQueue.main.async { [weak self] in
184
+ guard let self = self else { return }
185
+ self.player?.play()
186
+ self.isPlaying = true
187
+ self.startProgressReporting()
188
+ callback(nil, nil)
189
+ }
190
+ }
191
+
192
+ private func stop(callback: @escaping (Any?, String?) -> Void) {
193
+ DispatchQueue.main.async { [weak self] in
194
+ guard let self = self else { return }
195
+ self.player?.stop()
196
+ self.player = nil
197
+ self.playerDelegate = nil
198
+ self.isPlaying = false
199
+ self.stopProgressReporting()
200
+ callback(nil, nil)
201
+ }
202
+ }
203
+
204
+ private func seek(position: Double, callback: @escaping (Any?, String?) -> Void) {
205
+ DispatchQueue.main.async { [weak self] in
206
+ self?.player?.currentTime = position
207
+ callback(nil, nil)
208
+ }
209
+ }
210
+
211
+ private func setVolume(_ volume: Float, callback: @escaping (Any?, String?) -> Void) {
212
+ DispatchQueue.main.async { [weak self] in
213
+ self?.player?.volume = max(0, min(1, volume))
214
+ callback(nil, nil)
215
+ }
216
+ }
217
+
218
+ // MARK: - Progress Reporting
219
+
220
+ private func startProgressReporting() {
221
+ stopProgressReporting()
222
+ let link = CADisplayLink(target: self, selector: #selector(reportProgress))
223
+ // Report at ~4 Hz (every 15 frames at 60fps)
224
+ link.preferredFrameRateRange = CAFrameRateRange(minimum: 4, maximum: 4, preferred: 4)
225
+ link.add(to: .main, forMode: .common)
226
+ displayLink = link
227
+ }
228
+
229
+ private func stopProgressReporting() {
230
+ displayLink?.invalidate()
231
+ displayLink = nil
232
+ }
233
+
234
+ @objc private func reportProgress() {
235
+ guard let player = player, isPlaying else { return }
236
+ bridge?.sendGlobalEvent("audio:progress", payload: [
237
+ "currentTime": player.currentTime,
238
+ "duration": player.duration,
239
+ ])
240
+ }
241
+
242
+ // MARK: - Recording
243
+
244
+ private func startRecording(options: [String: Any], callback: @escaping (Any?, String?) -> Void) {
245
+ DispatchQueue.main.async { [weak self] in
246
+ guard let self = self else { return }
247
+
248
+ do {
249
+ try AVAudioSession.sharedInstance().setCategory(.playAndRecord, mode: .default)
250
+ try AVAudioSession.sharedInstance().setActive(true)
251
+ } catch {
252
+ callback(nil, "Failed to set audio session for recording: \(error.localizedDescription)")
253
+ return
254
+ }
255
+
256
+ let quality = options["quality"] as? String ?? "medium"
257
+ let format = options["format"] as? String ?? "m4a"
258
+
259
+ let ext = format == "wav" ? "wav" : "m4a"
260
+ let url = FileManager.default.temporaryDirectory
261
+ .appendingPathComponent(UUID().uuidString + ".\(ext)")
262
+
263
+ var settings: [String: Any] = [:]
264
+ if format == "wav" {
265
+ settings = [
266
+ AVFormatIDKey: Int(kAudioFormatLinearPCM),
267
+ AVSampleRateKey: quality == "high" ? 44100.0 : 22050.0,
268
+ AVNumberOfChannelsKey: 1,
269
+ AVLinearPCMBitDepthKey: 16,
270
+ AVLinearPCMIsFloatKey: false,
271
+ ]
272
+ } else {
273
+ let sampleRate: Double
274
+ let bitRate: Int
275
+ switch quality {
276
+ case "low": sampleRate = 22050; bitRate = 32000
277
+ case "high": sampleRate = 44100; bitRate = 128000
278
+ default: sampleRate = 44100; bitRate = 64000
279
+ }
280
+ settings = [
281
+ AVFormatIDKey: Int(kAudioFormatMPEG4AAC),
282
+ AVSampleRateKey: sampleRate,
283
+ AVNumberOfChannelsKey: 1,
284
+ AVEncoderAudioQualityKey: AVAudioQuality.medium.rawValue,
285
+ AVEncoderBitRateKey: bitRate,
286
+ ]
287
+ }
288
+
289
+ do {
290
+ let recorder = try AVAudioRecorder(url: url, settings: settings)
291
+ recorder.prepareToRecord()
292
+ recorder.record()
293
+ self.recorder = recorder
294
+ callback(["uri": url.absoluteString], nil)
295
+ } catch {
296
+ callback(nil, "Failed to start recording: \(error.localizedDescription)")
297
+ }
298
+ }
299
+ }
300
+
301
+ private func stopRecording(callback: @escaping (Any?, String?) -> Void) {
302
+ DispatchQueue.main.async { [weak self] in
303
+ guard let self = self, let recorder = self.recorder else {
304
+ callback(nil, "No active recording")
305
+ return
306
+ }
307
+ let duration = recorder.currentTime
308
+ let uri = recorder.url.absoluteString
309
+ recorder.stop()
310
+ self.recorder = nil
311
+ callback(["uri": uri, "duration": duration], nil)
312
+ }
313
+ }
314
+
315
+ private func pauseRecording(callback: @escaping (Any?, String?) -> Void) {
316
+ DispatchQueue.main.async { [weak self] in
317
+ self?.recorder?.pause()
318
+ callback(nil, nil)
319
+ }
320
+ }
321
+
322
+ private func resumeRecording(callback: @escaping (Any?, String?) -> Void) {
323
+ DispatchQueue.main.async { [weak self] in
324
+ self?.recorder?.record()
325
+ callback(nil, nil)
326
+ }
327
+ }
328
+
329
+ // MARK: - Status
330
+
331
+ private func getStatus(callback: @escaping (Any?, String?) -> Void) {
332
+ DispatchQueue.main.async { [weak self] in
333
+ guard let self = self else { callback(nil, nil); return }
334
+ var status: [String: Any] = [
335
+ "isPlaying": self.isPlaying,
336
+ "isRecording": self.recorder?.isRecording ?? false,
337
+ ]
338
+ if let player = self.player {
339
+ status["currentTime"] = player.currentTime
340
+ status["duration"] = player.duration
341
+ status["volume"] = player.volume
342
+ }
343
+ callback(status, nil)
344
+ }
345
+ }
346
+ }
347
+
348
+ // MARK: - AVAudioPlayerDelegate wrapper
349
+
350
+ private final class AudioPlayerDelegate: NSObject, AVAudioPlayerDelegate {
351
+ private let onComplete: (Bool) -> Void
352
+
353
+ init(onComplete: @escaping (Bool) -> Void) {
354
+ self.onComplete = onComplete
355
+ }
356
+
357
+ func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
358
+ onComplete(flag)
359
+ }
360
+
361
+ func audioPlayerDecodeErrorDidOccur(_ player: AVAudioPlayer, error: Error?) {
362
+ // Treat decode error as completion failure
363
+ onComplete(false)
364
+ }
365
+ }
366
+ #endif
@@ -0,0 +1,135 @@
1
+ #if canImport(UIKit)
2
+ import UIKit
3
+ import BackgroundTasks
4
+
5
+ /// Native module for scheduling background tasks using BGTaskScheduler.
6
+ ///
7
+ /// Methods:
8
+ /// - scheduleTask(taskId, type, options) — schedule a background task
9
+ /// - cancelTask(taskId) — cancel a specific task
10
+ /// - cancelAllTasks() — cancel all scheduled tasks
11
+ /// - completeTask(taskId) — signal task completion from JS
12
+ ///
13
+ /// Events:
14
+ /// - background:taskExecute — fired when a background task runs, payload: { taskId }
15
+ @available(iOS 13.0, *)
16
+ final class BackgroundTaskModule: NativeModule {
17
+ var moduleName: String { "BackgroundTask" }
18
+ private weak var bridge: NativeBridge?
19
+
20
+ /// Track active tasks so we can call setTaskCompleted from JS
21
+ private var activeTasks: [String: BGTask] = [:]
22
+
23
+ /// Registered task identifiers
24
+ private var registeredIdentifiers: Set<String> = []
25
+
26
+ init(bridge: NativeBridge) {
27
+ self.bridge = bridge
28
+ }
29
+
30
+ func invoke(method: String, args: [Any], callback: @escaping (Any?, String?) -> Void) {
31
+ switch method {
32
+ case "scheduleTask":
33
+ guard args.count >= 2,
34
+ let taskId = args[0] as? String,
35
+ let type = args[1] as? String else {
36
+ callback(nil, "scheduleTask: missing taskId or type")
37
+ return
38
+ }
39
+ let options = args.count >= 3 ? args[2] as? [String: Any] : nil
40
+ scheduleTask(taskId: taskId, type: type, options: options ?? [:], callback: callback)
41
+
42
+ case "cancelTask":
43
+ guard let taskId = args.first as? String else {
44
+ callback(nil, "cancelTask: missing taskId")
45
+ return
46
+ }
47
+ BGTaskScheduler.shared.cancel(taskIdentifier: taskId)
48
+ callback(nil, nil)
49
+
50
+ case "cancelAllTasks":
51
+ BGTaskScheduler.shared.cancelAllTaskRequests()
52
+ callback(nil, nil)
53
+
54
+ case "completeTask":
55
+ guard let taskId = args.first as? String else {
56
+ callback(nil, "completeTask: missing taskId")
57
+ return
58
+ }
59
+ let success = (args.count >= 2 ? args[1] as? Bool : nil) ?? true
60
+ if let task = activeTasks[taskId] {
61
+ task.setTaskCompleted(success: success)
62
+ activeTasks.removeValue(forKey: taskId)
63
+ }
64
+ callback(nil, nil)
65
+
66
+ case "registerTask":
67
+ guard let taskId = args.first as? String else {
68
+ callback(nil, "registerTask: missing taskId")
69
+ return
70
+ }
71
+ registerTaskHandler(taskId: taskId)
72
+ callback(nil, nil)
73
+
74
+ default:
75
+ callback(nil, "BackgroundTaskModule: Unknown method '\(method)'")
76
+ }
77
+ }
78
+
79
+ // MARK: - Private
80
+
81
+ private func registerTaskHandler(taskId: String) {
82
+ guard !registeredIdentifiers.contains(taskId) else { return }
83
+ registeredIdentifiers.insert(taskId)
84
+
85
+ BGTaskScheduler.shared.register(forTaskWithIdentifier: taskId, using: nil) { [weak self] task in
86
+ self?.handleTaskExecution(task: task)
87
+ }
88
+ }
89
+
90
+ private func handleTaskExecution(task: BGTask) {
91
+ let taskId = task.identifier
92
+ activeTasks[taskId] = task
93
+
94
+ task.expirationHandler = { [weak self] in
95
+ self?.activeTasks[taskId]?.setTaskCompleted(success: false)
96
+ self?.activeTasks.removeValue(forKey: taskId)
97
+ }
98
+
99
+ // Notify JS that the task is executing
100
+ let bridge = bridge
101
+ DispatchQueue.main.async {
102
+ bridge?.dispatchGlobalEvent("background:taskExecute", payload: ["taskId": taskId])
103
+ }
104
+ }
105
+
106
+ private func scheduleTask(taskId: String, type: String, options: [String: Any], callback: @escaping (Any?, String?) -> Void) {
107
+ // Ensure task handler is registered
108
+ registerTaskHandler(taskId: taskId)
109
+
110
+ let request: BGTaskRequest
111
+ if type == "processing" {
112
+ let processingRequest = BGProcessingTaskRequest(identifier: taskId)
113
+ processingRequest.requiresNetworkConnectivity = options["requiresNetworkConnectivity"] as? Bool ?? false
114
+ processingRequest.requiresExternalPower = options["requiresExternalPower"] as? Bool ?? false
115
+ request = processingRequest
116
+ } else {
117
+ // Default: app refresh
118
+ request = BGAppRefreshTaskRequest(identifier: taskId)
119
+ }
120
+
121
+ if let earliestBeginDate = options["earliestBeginDate"] as? Double {
122
+ request.earliestBeginDate = Date(timeIntervalSince1970: earliestBeginDate / 1000.0)
123
+ }
124
+
125
+ do {
126
+ try BGTaskScheduler.shared.submit(request)
127
+ callback(nil, nil)
128
+ } catch {
129
+ callback(nil, "Failed to schedule task: \(error.localizedDescription)")
130
+ }
131
+ }
132
+
133
+ func invokeSync(method: String, args: [Any]) -> Any? { nil }
134
+ }
135
+ #endif
@@ -0,0 +1,61 @@
1
+ #if canImport(UIKit)
2
+ import UIKit
3
+ import LocalAuthentication
4
+
5
+ /// Native module for biometric authentication (Face ID, Touch ID, Optic ID).
6
+ ///
7
+ /// Methods:
8
+ /// - getSupportedBiometry() -> "faceID" | "touchID" | "opticID" | "none"
9
+ /// - isAvailable() -> Bool
10
+ /// - authenticate(reason: String) -> { success: Bool, error?: String }
11
+ final class BiometryModule: NativeModule {
12
+ var moduleName: String { "Biometry" }
13
+
14
+ func invoke(method: String, args: [Any], callback: @escaping (Any?, String?) -> Void) {
15
+ switch method {
16
+ case "getSupportedBiometry":
17
+ callback(supportedBiometry(), nil)
18
+ case "isAvailable":
19
+ let context = LAContext()
20
+ var error: NSError?
21
+ let available = context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error)
22
+ callback(available, nil)
23
+ case "authenticate":
24
+ let reason = args.first as? String ?? "Authenticate"
25
+ authenticate(reason: reason, callback: callback)
26
+ default:
27
+ callback(nil, "BiometryModule: Unknown method '\(method)'")
28
+ }
29
+ }
30
+
31
+ // MARK: - Private helpers
32
+
33
+ private func authenticate(reason: String, callback: @escaping (Any?, String?) -> Void) {
34
+ let context = LAContext()
35
+ context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: reason) { success, error in
36
+ if success {
37
+ callback(["success": true], nil)
38
+ } else {
39
+ let msg = error?.localizedDescription ?? "Authentication failed"
40
+ callback(["success": false, "error": msg], nil)
41
+ }
42
+ }
43
+ }
44
+
45
+ private func supportedBiometry() -> String {
46
+ let context = LAContext()
47
+ var error: NSError?
48
+ guard context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) else {
49
+ return "none"
50
+ }
51
+ switch context.biometryType {
52
+ case .faceID: return "faceID"
53
+ case .touchID: return "touchID"
54
+ case .opticID: return "opticID"
55
+ default: return "none"
56
+ }
57
+ }
58
+
59
+ func invokeSync(method: String, args: [Any]) -> Any? { nil }
60
+ }
61
+ #endif