@thelacanians/vue-native-cli 0.4.3 → 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 +34 -17
  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,215 @@
1
+ #if canImport(UIKit)
2
+ import UIKit
3
+ import UserNotifications
4
+
5
+ /// Native module for local and remote (push) notifications.
6
+ ///
7
+ /// Methods:
8
+ /// - requestPermission() -> Bool
9
+ /// - getPermissionStatus() -> "granted"|"denied"|"notDetermined"
10
+ /// - scheduleLocal(notification: Object) -> notificationId: String
11
+ /// - cancel(id: String)
12
+ /// - cancelAll()
13
+ /// - registerForPush() -> Void (registers for APNS remote notifications)
14
+ /// - getToken() -> String? (returns cached APNS device token)
15
+ ///
16
+ /// Global events dispatched on bridge:
17
+ /// "notification:received" -- when a notification arrives in foreground or is tapped
18
+ /// "push:token" -- when APNS device token is received { token }
19
+ /// "push:received" -- when a remote push notification arrives { title, body, data }
20
+ final class NotificationsModule: NativeModule {
21
+ var moduleName: String { "Notifications" }
22
+ private weak var bridge: NativeBridge?
23
+
24
+ /// Serial queue for thread-safe access to the device token.
25
+ private let tokenQueue = DispatchQueue(label: "com.vuenative.notifications.token")
26
+ /// Backing storage for deviceToken. Access only via the computed property.
27
+ private var _deviceToken: String?
28
+ /// Cached APNS device token (hex string). Thread-safe.
29
+ private var deviceToken: String? {
30
+ get { tokenQueue.sync { _deviceToken } }
31
+ set { tokenQueue.sync { _deviceToken = newValue } }
32
+ }
33
+
34
+ init(bridge: NativeBridge) {
35
+ self.bridge = bridge
36
+ setupForegroundHandler()
37
+ }
38
+
39
+ private func setupForegroundHandler() {
40
+ UNUserNotificationCenter.current().delegate = NotificationCenterDelegate.shared
41
+ // Capture bridge weakly; dispatch to main actor because dispatchGlobalEvent is @MainActor.
42
+ let weakBridge = bridge
43
+ NotificationCenterDelegate.shared.onNotification = { payload in
44
+ DispatchQueue.main.async {
45
+ weakBridge?.dispatchGlobalEvent("notification:received", payload: payload)
46
+ }
47
+ }
48
+ NotificationCenterDelegate.shared.onPushReceived = { payload in
49
+ DispatchQueue.main.async {
50
+ weakBridge?.dispatchGlobalEvent("push:received", payload: payload)
51
+ }
52
+ }
53
+ }
54
+
55
+ func invoke(method: String, args: [Any], callback: @escaping (Any?, String?) -> Void) {
56
+ switch method {
57
+ case "requestPermission":
58
+ UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { granted, error in
59
+ callback(granted, error?.localizedDescription)
60
+ }
61
+ case "getPermissionStatus":
62
+ UNUserNotificationCenter.current().getNotificationSettings { settings in
63
+ callback(self.statusString(settings.authorizationStatus), nil)
64
+ }
65
+ case "scheduleLocal":
66
+ guard let notification = args.first as? [String: Any] else {
67
+ callback(nil, "NotificationsModule: invalid notification object"); return
68
+ }
69
+ scheduleLocal(notification, callback: callback)
70
+ case "cancel":
71
+ if let id = args.first as? String {
72
+ UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: [id])
73
+ }
74
+ callback(nil, nil)
75
+ case "cancelAll":
76
+ UNUserNotificationCenter.current().removeAllPendingNotificationRequests()
77
+ callback(nil, nil)
78
+ case "registerForPush":
79
+ DispatchQueue.main.async {
80
+ UIApplication.shared.registerForRemoteNotifications()
81
+ }
82
+ callback(true, nil)
83
+ case "getToken":
84
+ callback(deviceToken, nil)
85
+ default:
86
+ callback(nil, "NotificationsModule: Unknown method '\(method)'")
87
+ }
88
+ }
89
+
90
+ // MARK: - Push token handling (called from AppDelegate)
91
+
92
+ /// Call this from your AppDelegate's `application(_:didRegisterForRemoteNotificationsWithDeviceToken:)`
93
+ func didRegisterForRemoteNotifications(deviceToken data: Data) {
94
+ let token = data.map { String(format: "%02x", $0) }.joined()
95
+ self.deviceToken = token
96
+ DispatchQueue.main.async { [weak self] in
97
+ self?.bridge?.dispatchGlobalEvent("push:token", payload: ["token": token])
98
+ }
99
+ }
100
+
101
+ /// Call this from your AppDelegate's `application(_:didFailToRegisterForRemoteNotificationsWithError:)`
102
+ func didFailToRegisterForRemoteNotifications(error: Error) {
103
+ DispatchQueue.main.async { [weak self] in
104
+ self?.bridge?.dispatchGlobalEvent("push:error", payload: [
105
+ "message": error.localizedDescription
106
+ ])
107
+ }
108
+ }
109
+
110
+ // MARK: - Schedule local notification
111
+
112
+ private func scheduleLocal(_ notification: [String: Any], callback: @escaping (Any?, String?) -> Void) {
113
+ let content = UNMutableNotificationContent()
114
+ content.title = notification["title"] as? String ?? ""
115
+ content.body = notification["body"] as? String ?? ""
116
+
117
+ if let sound = notification["sound"] as? String, sound == "default" {
118
+ content.sound = .default
119
+ }
120
+ if let badge = notification["badge"] as? Int {
121
+ content.badge = NSNumber(value: badge)
122
+ }
123
+ if let data = notification["data"] as? [String: Any] {
124
+ content.userInfo = data
125
+ }
126
+
127
+ let delay = max((notification["delay"] as? Double) ?? 0.1, 0.1)
128
+ let trigger = UNTimeIntervalNotificationTrigger(timeInterval: delay, repeats: false)
129
+ let id = (notification["id"] as? String) ?? UUID().uuidString
130
+ let request = UNNotificationRequest(identifier: id, content: content, trigger: trigger)
131
+
132
+ UNUserNotificationCenter.current().add(request) { error in
133
+ if let error = error {
134
+ callback(nil, error.localizedDescription)
135
+ } else {
136
+ callback(id, nil)
137
+ }
138
+ }
139
+ }
140
+
141
+ // MARK: - Status helper
142
+
143
+ private func statusString(_ status: UNAuthorizationStatus) -> String {
144
+ switch status {
145
+ case .authorized, .provisional, .ephemeral: return "granted"
146
+ case .denied: return "denied"
147
+ case .notDetermined: return "notDetermined"
148
+ @unknown default: return "notDetermined"
149
+ }
150
+ }
151
+
152
+ func invokeSync(method: String, args: [Any]) -> Any? { nil }
153
+ }
154
+
155
+ // MARK: - UNUserNotificationCenterDelegate
156
+
157
+ /// Singleton delegate that forwards foreground and tapped notifications
158
+ /// to whoever has set `onNotification`.
159
+ private final class NotificationCenterDelegate: NSObject, UNUserNotificationCenterDelegate {
160
+ static let shared = NotificationCenterDelegate()
161
+ /// Called for local notifications and tapped notifications.
162
+ var onNotification: (([String: Any]) -> Void)?
163
+ /// Called specifically for remote push notifications arriving in foreground.
164
+ var onPushReceived: (([String: Any]) -> Void)?
165
+
166
+ /// Show banners/sounds/badges even when the app is in foreground.
167
+ func userNotificationCenter(_ center: UNUserNotificationCenter,
168
+ willPresent notification: UNNotification,
169
+ withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
170
+ let request = notification.request
171
+ if request.trigger is UNPushNotificationTrigger {
172
+ onPushReceived?(makePushPayload(from: request))
173
+ } else {
174
+ onNotification?(makePayload(from: request))
175
+ }
176
+ completionHandler([.banner, .sound, .badge])
177
+ }
178
+
179
+ /// Called when the user taps a notification.
180
+ func userNotificationCenter(_ center: UNUserNotificationCenter,
181
+ didReceive response: UNNotificationResponse,
182
+ withCompletionHandler completionHandler: @escaping () -> Void) {
183
+ let request = response.notification.request
184
+ if request.trigger is UNPushNotificationTrigger {
185
+ var payload = makePushPayload(from: request)
186
+ payload["action"] = response.actionIdentifier
187
+ onPushReceived?(payload)
188
+ } else {
189
+ var payload = makePayload(from: request)
190
+ payload["action"] = response.actionIdentifier
191
+ onNotification?(payload)
192
+ }
193
+ completionHandler()
194
+ }
195
+
196
+ private func makePayload(from request: UNNotificationRequest) -> [String: Any] {
197
+ [
198
+ "id": request.identifier,
199
+ "title": request.content.title,
200
+ "body": request.content.body,
201
+ "data": request.content.userInfo
202
+ ]
203
+ }
204
+
205
+ private func makePushPayload(from request: UNNotificationRequest) -> [String: Any] {
206
+ [
207
+ "id": request.identifier,
208
+ "title": request.content.title,
209
+ "body": request.content.body,
210
+ "data": request.content.userInfo,
211
+ "remote": true
212
+ ]
213
+ }
214
+ }
215
+ #endif
@@ -0,0 +1,281 @@
1
+ #if canImport(UIKit)
2
+ import UIKit
3
+ import CommonCrypto
4
+
5
+ /// Native module for Over-The-Air (OTA) JS bundle updates.
6
+ ///
7
+ /// Methods:
8
+ /// - checkForUpdate(serverUrl) — check for available updates
9
+ /// - downloadUpdate(url, hash) — download a new bundle and verify integrity
10
+ /// - applyUpdate() — swap to downloaded bundle on next launch
11
+ /// - rollback() — revert to the embedded bundle
12
+ /// - getCurrentVersion() — get current bundle version info
13
+ ///
14
+ /// Events:
15
+ /// - ota:downloadProgress — payload: { progress: 0.0-1.0, bytesDownloaded, totalBytes }
16
+ final class OTAModule: NativeModule {
17
+ var moduleName: String { "OTA" }
18
+
19
+ private let defaults = UserDefaults.standard
20
+ private let keyPrefix = "VueNative.OTA."
21
+
22
+ // UserDefaults keys
23
+ private var currentVersionKey: String { keyPrefix + "currentVersion" }
24
+ private var bundlePathKey: String { keyPrefix + "bundlePath" }
25
+ private var previousBundlePathKey: String { keyPrefix + "previousBundlePath" }
26
+ private var previousVersionKey: String { keyPrefix + "previousVersion" }
27
+
28
+ private weak var bridge: NativeBridge?
29
+
30
+ init(bridge: NativeBridge) {
31
+ self.bridge = bridge
32
+ }
33
+
34
+ func invoke(method: String, args: [Any], callback: @escaping (Any?, String?) -> Void) {
35
+ switch method {
36
+ case "checkForUpdate":
37
+ guard let serverUrl = args.first as? String else {
38
+ callback(nil, "checkForUpdate: missing serverUrl")
39
+ return
40
+ }
41
+ checkForUpdate(serverUrl: serverUrl, callback: callback)
42
+
43
+ case "downloadUpdate":
44
+ guard args.count >= 1,
45
+ let url = args[0] as? String else {
46
+ callback(nil, "downloadUpdate: missing url")
47
+ return
48
+ }
49
+ let expectedHash = args.count >= 2 ? args[1] as? String : nil
50
+ downloadUpdate(url: url, expectedHash: expectedHash, callback: callback)
51
+
52
+ case "applyUpdate":
53
+ applyUpdate(callback: callback)
54
+
55
+ case "rollback":
56
+ rollback(callback: callback)
57
+
58
+ case "getCurrentVersion":
59
+ getCurrentVersion(callback: callback)
60
+
61
+ default:
62
+ callback(nil, "OTAModule: Unknown method '\(method)'")
63
+ }
64
+ }
65
+
66
+ // MARK: - Check for update
67
+
68
+ private func checkForUpdate(serverUrl: String, callback: @escaping (Any?, String?) -> Void) {
69
+ guard let url = URL(string: serverUrl) else {
70
+ callback(nil, "Invalid server URL")
71
+ return
72
+ }
73
+
74
+ let currentVersion = defaults.string(forKey: currentVersionKey) ?? "0"
75
+
76
+ var request = URLRequest(url: url)
77
+ request.httpMethod = "GET"
78
+ request.setValue(currentVersion, forHTTPHeaderField: "X-Current-Version")
79
+ request.setValue("ios", forHTTPHeaderField: "X-Platform")
80
+ request.setValue(Bundle.main.bundleIdentifier ?? "unknown", forHTTPHeaderField: "X-App-Id")
81
+
82
+ URLSession.shared.dataTask(with: request) { data, response, error in
83
+ if let error = error {
84
+ callback(nil, "Network error: \(error.localizedDescription)")
85
+ return
86
+ }
87
+
88
+ guard let data = data,
89
+ let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else {
90
+ callback(nil, "Invalid response from update server")
91
+ return
92
+ }
93
+
94
+ let updateAvailable = json["updateAvailable"] as? Bool ?? false
95
+ let result: [String: Any] = [
96
+ "updateAvailable": updateAvailable,
97
+ "version": json["version"] as? String ?? "",
98
+ "downloadUrl": json["downloadUrl"] as? String ?? "",
99
+ "hash": json["hash"] as? String ?? "",
100
+ "size": json["size"] as? Int ?? 0,
101
+ "releaseNotes": json["releaseNotes"] as? String ?? "",
102
+ ]
103
+ callback(result, nil)
104
+ }.resume()
105
+ }
106
+
107
+ // MARK: - Download update
108
+
109
+ private func downloadUpdate(url: String, expectedHash: String?, callback: @escaping (Any?, String?) -> Void) {
110
+ guard let downloadUrl = URL(string: url) else {
111
+ callback(nil, "Invalid download URL")
112
+ return
113
+ }
114
+
115
+ let session = URLSession(configuration: .default, delegate: DownloadDelegate(bridge: bridge), delegateQueue: nil)
116
+ let task = session.downloadTask(with: downloadUrl) { [weak self] tempUrl, response, error in
117
+ guard let self = self else { return }
118
+
119
+ if let error = error {
120
+ callback(nil, "Download failed: \(error.localizedDescription)")
121
+ return
122
+ }
123
+
124
+ guard let tempUrl = tempUrl else {
125
+ callback(nil, "Download failed: no file received")
126
+ return
127
+ }
128
+
129
+ do {
130
+ // Read downloaded data
131
+ let data = try Data(contentsOf: tempUrl)
132
+
133
+ // Verify hash if provided
134
+ if let expectedHash = expectedHash, !expectedHash.isEmpty {
135
+ let actualHash = self.sha256(data: data)
136
+ if actualHash.lowercased() != expectedHash.lowercased() {
137
+ callback(nil, "Bundle integrity check failed. Expected: \(expectedHash), got: \(actualHash)")
138
+ return
139
+ }
140
+ }
141
+
142
+ // Save to Documents directory
143
+ let docsDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
144
+ let bundleDir = docsDir.appendingPathComponent("VueNativeOTA", isDirectory: true)
145
+ try FileManager.default.createDirectory(at: bundleDir, withIntermediateDirectories: true)
146
+
147
+ let bundlePath = bundleDir.appendingPathComponent("bundle.js")
148
+
149
+ // Remove old pending download if exists
150
+ if FileManager.default.fileExists(atPath: bundlePath.path) {
151
+ try FileManager.default.removeItem(at: bundlePath)
152
+ }
153
+
154
+ try data.write(to: bundlePath)
155
+
156
+ // Store the path for applyUpdate
157
+ self.defaults.set(bundlePath.path, forKey: self.keyPrefix + "pendingBundlePath")
158
+
159
+ callback(["path": bundlePath.path, "size": data.count], nil)
160
+ } catch {
161
+ callback(nil, "Failed to save bundle: \(error.localizedDescription)")
162
+ }
163
+ }
164
+ task.resume()
165
+ }
166
+
167
+ // MARK: - Apply update
168
+
169
+ private func applyUpdate(callback: @escaping (Any?, String?) -> Void) {
170
+ guard let pendingPath = defaults.string(forKey: keyPrefix + "pendingBundlePath"),
171
+ FileManager.default.fileExists(atPath: pendingPath) else {
172
+ callback(nil, "No pending update to apply")
173
+ return
174
+ }
175
+
176
+ // Save current bundle path for rollback
177
+ if let currentPath = defaults.string(forKey: bundlePathKey) {
178
+ defaults.set(currentPath, forKey: previousBundlePathKey)
179
+ }
180
+ if let currentVersion = defaults.string(forKey: currentVersionKey) {
181
+ defaults.set(currentVersion, forKey: previousVersionKey)
182
+ }
183
+
184
+ // Set the new bundle as current
185
+ defaults.set(pendingPath, forKey: bundlePathKey)
186
+ defaults.removeObject(forKey: keyPrefix + "pendingBundlePath")
187
+
188
+ // Increment version tracker
189
+ let currentVersion = defaults.integer(forKey: currentVersionKey)
190
+ defaults.set(currentVersion + 1, forKey: currentVersionKey)
191
+
192
+ callback(["applied": true], nil)
193
+ }
194
+
195
+ // MARK: - Rollback
196
+
197
+ private func rollback(callback: @escaping (Any?, String?) -> Void) {
198
+ guard let previousPath = defaults.string(forKey: previousBundlePathKey) else {
199
+ // Rollback to embedded bundle
200
+ defaults.removeObject(forKey: bundlePathKey)
201
+ if let prevVersion = defaults.string(forKey: previousVersionKey) {
202
+ defaults.set(prevVersion, forKey: currentVersionKey)
203
+ } else {
204
+ defaults.removeObject(forKey: currentVersionKey)
205
+ }
206
+ callback(["rolledBack": true, "toEmbedded": true], nil)
207
+ return
208
+ }
209
+
210
+ defaults.set(previousPath, forKey: bundlePathKey)
211
+ if let prevVersion = defaults.string(forKey: previousVersionKey) {
212
+ defaults.set(prevVersion, forKey: currentVersionKey)
213
+ }
214
+ defaults.removeObject(forKey: previousBundlePathKey)
215
+ defaults.removeObject(forKey: previousVersionKey)
216
+
217
+ callback(["rolledBack": true, "toEmbedded": false], nil)
218
+ }
219
+
220
+ // MARK: - Get current version
221
+
222
+ private func getCurrentVersion(callback: @escaping (Any?, String?) -> Void) {
223
+ let version = defaults.string(forKey: currentVersionKey) ?? "embedded"
224
+ let bundlePath = defaults.string(forKey: bundlePathKey)
225
+ let isUsingOTA = bundlePath != nil && FileManager.default.fileExists(atPath: bundlePath!)
226
+
227
+ callback([
228
+ "version": version,
229
+ "isUsingOTA": isUsingOTA,
230
+ "bundlePath": bundlePath ?? "",
231
+ ], nil)
232
+ }
233
+
234
+ // MARK: - SHA-256
235
+
236
+ private func sha256(data: Data) -> String {
237
+ var hash = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
238
+ data.withUnsafeBytes { buffer in
239
+ _ = CC_SHA256(buffer.baseAddress, CC_LONG(data.count), &hash)
240
+ }
241
+ return hash.map { String(format: "%02x", $0) }.joined()
242
+ }
243
+
244
+ func invokeSync(method: String, args: [Any]) -> Any? { nil }
245
+ }
246
+
247
+ // MARK: - Download delegate for progress reporting
248
+
249
+ private class DownloadDelegate: NSObject, URLSessionDownloadDelegate {
250
+ private weak var bridge: NativeBridge?
251
+
252
+ init(bridge: NativeBridge?) {
253
+ self.bridge = bridge
254
+ }
255
+
256
+ func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask,
257
+ didWriteData bytesWritten: Int64, totalBytesWritten: Int64,
258
+ totalBytesExpectedToWrite: Int64) {
259
+ let progress: Double
260
+ if totalBytesExpectedToWrite > 0 {
261
+ progress = Double(totalBytesWritten) / Double(totalBytesExpectedToWrite)
262
+ } else {
263
+ progress = 0
264
+ }
265
+
266
+ let bridge = bridge
267
+ DispatchQueue.main.async {
268
+ bridge?.dispatchGlobalEvent("ota:downloadProgress", payload: [
269
+ "progress": progress,
270
+ "bytesDownloaded": totalBytesWritten,
271
+ "totalBytes": totalBytesExpectedToWrite,
272
+ ])
273
+ }
274
+ }
275
+
276
+ func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask,
277
+ didFinishDownloadingTo location: URL) {
278
+ // Handled in the completion handler of the download task
279
+ }
280
+ }
281
+ #endif
@@ -0,0 +1,138 @@
1
+ #if canImport(UIKit)
2
+ import UIKit
3
+
4
+ /// Native module for performance profiling.
5
+ /// Tracks FPS via CADisplayLink, memory usage via task_info, and bridge operation counts.
6
+ /// Dispatches `perf:metrics` global events every 1 second while profiling is active.
7
+ final class PerformanceModule: NativeModule {
8
+
9
+ let moduleName = "Performance"
10
+
11
+ private weak var bridge: NativeBridge?
12
+ private var displayLink: CADisplayLink?
13
+ private var isProfiling = false
14
+
15
+ // FPS tracking
16
+ private var frameCount = 0
17
+ private var lastTimestamp: CFTimeInterval = 0
18
+ private var currentFPS: Double = 0
19
+
20
+ // Metrics timer
21
+ private var metricsTimer: Timer?
22
+
23
+ // Bridge operation count
24
+ private var bridgeOpsCount: Int = 0
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 "startProfiling":
33
+ startProfiling(callback: callback)
34
+ case "stopProfiling":
35
+ stopProfiling(callback: callback)
36
+ case "getMetrics":
37
+ let metrics = collectMetrics()
38
+ callback(metrics, nil)
39
+ default:
40
+ callback(nil, "PerformanceModule: unknown method '\(method)'")
41
+ }
42
+ }
43
+
44
+ // MARK: - Start / Stop
45
+
46
+ private func startProfiling(callback: @escaping (Any?, String?) -> Void) {
47
+ guard !isProfiling else { callback(true, nil); return }
48
+ isProfiling = true
49
+ frameCount = 0
50
+ lastTimestamp = 0
51
+ currentFPS = 0
52
+ bridgeOpsCount = 0
53
+
54
+ DispatchQueue.main.async { [weak self] in
55
+ guard let self = self else { return }
56
+
57
+ // CADisplayLink for FPS measurement
58
+ let link = CADisplayLink(target: self, selector: #selector(self.handleDisplayLink(_:)))
59
+ link.add(to: .main, forMode: .common)
60
+ self.displayLink = link
61
+
62
+ // Timer for periodic metrics dispatch (every 1 second)
63
+ self.metricsTimer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] _ in
64
+ self?.dispatchMetrics()
65
+ }
66
+
67
+ callback(true, nil)
68
+ }
69
+ }
70
+
71
+ private func stopProfiling(callback: @escaping (Any?, String?) -> Void) {
72
+ guard isProfiling else { callback(true, nil); return }
73
+ isProfiling = false
74
+
75
+ DispatchQueue.main.async { [weak self] in
76
+ self?.displayLink?.invalidate()
77
+ self?.displayLink = nil
78
+ self?.metricsTimer?.invalidate()
79
+ self?.metricsTimer = nil
80
+ callback(true, nil)
81
+ }
82
+ }
83
+
84
+ // MARK: - CADisplayLink handler
85
+
86
+ @objc private func handleDisplayLink(_ link: CADisplayLink) {
87
+ if lastTimestamp == 0 {
88
+ lastTimestamp = link.timestamp
89
+ frameCount = 0
90
+ return
91
+ }
92
+
93
+ frameCount += 1
94
+ let elapsed = link.timestamp - lastTimestamp
95
+
96
+ // Calculate FPS every 0.5 seconds for smoother readings
97
+ if elapsed >= 0.5 {
98
+ currentFPS = Double(frameCount) / elapsed
99
+ frameCount = 0
100
+ lastTimestamp = link.timestamp
101
+ }
102
+ }
103
+
104
+ // MARK: - Metrics collection
105
+
106
+ private func collectMetrics() -> [String: Any] {
107
+ return [
108
+ "fps": round(currentFPS * 10) / 10,
109
+ "memoryMB": getMemoryUsageMB(),
110
+ "bridgeOps": bridgeOpsCount,
111
+ "timestamp": Date().timeIntervalSince1970 * 1000,
112
+ ]
113
+ }
114
+
115
+ private func dispatchMetrics() {
116
+ guard isProfiling else { return }
117
+ bridgeOpsCount += 1 // Count the metrics dispatch itself
118
+ let metrics = collectMetrics()
119
+ bridge?.dispatchGlobalEvent("perf:metrics", payload: metrics)
120
+ }
121
+
122
+ // MARK: - Memory measurement
123
+
124
+ private func getMemoryUsageMB() -> Double {
125
+ var info = mach_task_basic_info()
126
+ var count = mach_msg_type_number_t(MemoryLayout<mach_task_basic_info>.size) / 4
127
+ let result = withUnsafeMutablePointer(to: &info) {
128
+ $0.withMemoryRebound(to: integer_t.self, capacity: Int(count)) {
129
+ task_info(mach_task_self_, task_flavor_t(MACH_TASK_BASIC_INFO), $0, &count)
130
+ }
131
+ }
132
+ if result == KERN_SUCCESS {
133
+ return Double(info.resident_size) / (1024 * 1024)
134
+ }
135
+ return 0
136
+ }
137
+ }
138
+ #endif