zixflow-reactnative 1.1.3 → 1.1.5

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 (239) hide show
  1. package/README.md +631 -261
  2. package/android/build.gradle +18 -1
  3. package/android/consumer-rules.pro +3 -2
  4. package/android/gradle.properties +1 -1
  5. package/android/src/main/java/com/zixflow/reactnative/sdk/NativeZixflowModule.kt +41 -5
  6. package/android/src/main/java/com/zixflow/reactnative/sdk/ZixflowReactNativePackage.kt +25 -1
  7. package/android/src/main/java/com/zixflow/reactnative/sdk/constant/Keys.kt +19 -0
  8. package/android/src/main/java/com/zixflow/reactnative/sdk/geofence/NativeGeofenceModule.kt +65 -0
  9. package/android/src/main/java/com/zixflow/reactnative/sdk/liveactivities/NativeLiveActivitiesModule.kt +310 -0
  10. package/android/src/main/java/com/zixflow/reactnative/sdk/messaginginapp/NativeMessagingInAppModule.kt +150 -0
  11. package/android/src/main/java/com/zixflow/reactnative/sdk/messaginginapp/ReactInboxEventListener.kt +99 -0
  12. package/android/src/main/java/com/zixflow/reactnative/sdk/messaginginbox/NotificationInboxBellViewManager.kt +38 -0
  13. package/android/src/main/java/com/zixflow/reactnative/sdk/messaginginbox/NotificationInboxViewManager.kt +30 -0
  14. package/android/src/main/java/com/zixflow/reactnative/sdk/messaginginbox/ReactComposeHostView.kt +96 -0
  15. package/android/src/main/java/com/zixflow/reactnative/sdk/messaginginbox/ReactNotificationInboxBellView.kt +48 -0
  16. package/android/src/main/java/com/zixflow/reactnative/sdk/messaginginbox/ReactNotificationInboxView.kt +17 -0
  17. package/android/src/main/java/com/zixflow/reactnative/sdk/messagingpush/NativeMessagingPushModule.kt +11 -1
  18. package/android/src/main/res/values/zixflow_config.xml +1 -1
  19. package/android/zixflow-core.gradle +15 -2
  20. package/docs/cocoapods-deployment-target-normalization.md +139 -0
  21. package/ios/cocoapods_deployment_target.rb +432 -0
  22. package/ios/wrappers/NativeZixflow.mm +37 -0
  23. package/ios/wrappers/NativeZixflow.swift +135 -1
  24. package/ios/wrappers/ZixflowReactNativeDeepLinkRequestStore.swift +123 -0
  25. package/ios/wrappers/ZixflowReactNativeDeepLinkRouter.swift +312 -0
  26. package/ios/wrappers/geofence/NativeGeofence.mm +46 -0
  27. package/ios/wrappers/geofence/NativeGeofence.swift +27 -0
  28. package/ios/wrappers/inapp/NativeMessagingInApp.mm +22 -0
  29. package/ios/wrappers/inapp/NativeMessagingInApp.swift +91 -0
  30. package/ios/wrappers/inbox/InboxHostContainment.swift +40 -0
  31. package/ios/wrappers/inbox/RCTNotificationInboxBellNative.h +12 -0
  32. package/ios/wrappers/inbox/RCTNotificationInboxBellNative.mm +98 -0
  33. package/ios/wrappers/inbox/RCTNotificationInboxViewNative.h +12 -0
  34. package/ios/wrappers/inbox/RCTNotificationInboxViewNative.mm +78 -0
  35. package/ios/wrappers/inbox/ReactInboxEventListener.swift +76 -0
  36. package/ios/wrappers/inbox/ReactNotificationInboxBellView.h +22 -0
  37. package/ios/wrappers/inbox/ReactNotificationInboxBellView.swift +109 -0
  38. package/ios/wrappers/inbox/ReactNotificationInboxView.h +22 -0
  39. package/ios/wrappers/inbox/ReactNotificationInboxView.swift +69 -0
  40. package/ios/wrappers/liveactivities/NativeLiveActivities.mm +83 -0
  41. package/ios/wrappers/liveactivities/NativeLiveActivities.swift +390 -0
  42. package/ios/wrappers/location/NativeLocation.swift +7 -5
  43. package/ios/wrappers/utils/ZixflowConstants.swift +4 -0
  44. package/ios/wrappers/zixflow_reactnative.h +6 -0
  45. package/lib/commonjs/components/NotificationInboxBellView.js +49 -0
  46. package/lib/commonjs/components/NotificationInboxBellView.js.map +1 -0
  47. package/lib/commonjs/components/NotificationInboxView.js +36 -0
  48. package/lib/commonjs/components/NotificationInboxView.js.map +1 -0
  49. package/lib/commonjs/components/index.js +14 -0
  50. package/lib/commonjs/components/index.js.map +1 -1
  51. package/lib/commonjs/index.js +81 -4
  52. package/lib/commonjs/index.js.map +1 -1
  53. package/lib/commonjs/notification-inbox.js +1 -0
  54. package/lib/commonjs/notification-inbox.js.map +1 -1
  55. package/lib/commonjs/specs/components/NotificationInboxBellNativeComponent.ts +24 -0
  56. package/lib/commonjs/specs/components/NotificationInboxViewNativeComponent.ts +22 -0
  57. package/lib/commonjs/specs/modules/NativeZixflow.js.map +1 -1
  58. package/lib/commonjs/specs/modules/NativeZixflowGeofence.js +15 -0
  59. package/lib/commonjs/specs/modules/NativeZixflowGeofence.js.map +1 -0
  60. package/lib/commonjs/specs/modules/NativeZixflowLiveActivities.js +23 -0
  61. package/lib/commonjs/specs/modules/NativeZixflowLiveActivities.js.map +1 -0
  62. package/lib/commonjs/specs/modules/NativeZixflowMessagingInApp.js +3 -1
  63. package/lib/commonjs/specs/modules/NativeZixflowMessagingInApp.js.map +1 -1
  64. package/lib/commonjs/specs/modules/NativeZixflowMessagingPush.js +3 -1
  65. package/lib/commonjs/specs/modules/NativeZixflowMessagingPush.js.map +1 -1
  66. package/lib/commonjs/types/data-pipelines.js +13 -1
  67. package/lib/commonjs/types/data-pipelines.js.map +1 -1
  68. package/lib/commonjs/types/in-app.js +24 -1
  69. package/lib/commonjs/types/in-app.js.map +1 -1
  70. package/lib/commonjs/types/inbox.js +79 -0
  71. package/lib/commonjs/types/inbox.js.map +1 -0
  72. package/lib/commonjs/types/index.js +22 -0
  73. package/lib/commonjs/types/index.js.map +1 -1
  74. package/lib/commonjs/types/live-activities.js +85 -0
  75. package/lib/commonjs/types/live-activities.js.map +1 -0
  76. package/lib/commonjs/utils/param-validation.js +76 -1
  77. package/lib/commonjs/utils/param-validation.js.map +1 -1
  78. package/lib/commonjs/zixflow-cdp.js +99 -1
  79. package/lib/commonjs/zixflow-cdp.js.map +1 -1
  80. package/lib/commonjs/zixflow-geofence.js +55 -0
  81. package/lib/commonjs/zixflow-geofence.js.map +1 -0
  82. package/lib/commonjs/zixflow-inapp.js +120 -0
  83. package/lib/commonjs/zixflow-inapp.js.map +1 -1
  84. package/lib/commonjs/zixflow-liveactivities.js +85 -0
  85. package/lib/commonjs/zixflow-liveactivities.js.map +1 -0
  86. package/lib/module/components/NotificationInboxBellView.js +44 -0
  87. package/lib/module/components/NotificationInboxBellView.js.map +1 -0
  88. package/lib/module/components/NotificationInboxView.js +31 -0
  89. package/lib/module/components/NotificationInboxView.js.map +1 -0
  90. package/lib/module/components/index.js +5 -0
  91. package/lib/module/components/index.js.map +1 -1
  92. package/lib/module/index.js +8 -3
  93. package/lib/module/index.js.map +1 -1
  94. package/lib/module/notification-inbox.js +1 -1
  95. package/lib/module/notification-inbox.js.map +1 -1
  96. package/lib/module/specs/components/NotificationInboxBellNativeComponent.ts +24 -0
  97. package/lib/module/specs/components/NotificationInboxViewNativeComponent.ts +22 -0
  98. package/lib/module/specs/modules/NativeZixflow.js.map +1 -1
  99. package/lib/module/specs/modules/NativeZixflowGeofence.js +13 -0
  100. package/lib/module/specs/modules/NativeZixflowGeofence.js.map +1 -0
  101. package/lib/module/specs/modules/NativeZixflowLiveActivities.js +21 -0
  102. package/lib/module/specs/modules/NativeZixflowLiveActivities.js.map +1 -0
  103. package/lib/module/specs/modules/NativeZixflowMessagingInApp.js +3 -1
  104. package/lib/module/specs/modules/NativeZixflowMessagingInApp.js.map +1 -1
  105. package/lib/module/specs/modules/NativeZixflowMessagingPush.js +3 -1
  106. package/lib/module/specs/modules/NativeZixflowMessagingPush.js.map +1 -1
  107. package/lib/module/types/data-pipelines.js +13 -0
  108. package/lib/module/types/data-pipelines.js.map +1 -1
  109. package/lib/module/types/in-app.js +24 -0
  110. package/lib/module/types/in-app.js.map +1 -1
  111. package/lib/module/types/inbox.js +75 -0
  112. package/lib/module/types/inbox.js.map +1 -0
  113. package/lib/module/types/index.js +2 -0
  114. package/lib/module/types/index.js.map +1 -1
  115. package/lib/module/types/live-activities.js +88 -0
  116. package/lib/module/types/live-activities.js.map +1 -0
  117. package/lib/module/utils/param-validation.js +76 -1
  118. package/lib/module/utils/param-validation.js.map +1 -1
  119. package/lib/module/zixflow-cdp.js +99 -1
  120. package/lib/module/zixflow-cdp.js.map +1 -1
  121. package/lib/module/zixflow-geofence.js +50 -0
  122. package/lib/module/zixflow-geofence.js.map +1 -0
  123. package/lib/module/zixflow-inapp.js +99 -1
  124. package/lib/module/zixflow-inapp.js.map +1 -1
  125. package/lib/module/zixflow-liveactivities.js +80 -0
  126. package/lib/module/zixflow-liveactivities.js.map +1 -0
  127. package/lib/typescript/commonjs/components/NotificationInboxBellView.d.ts +39 -0
  128. package/lib/typescript/commonjs/components/NotificationInboxBellView.d.ts.map +1 -0
  129. package/lib/typescript/commonjs/components/NotificationInboxView.d.ts +25 -0
  130. package/lib/typescript/commonjs/components/NotificationInboxView.d.ts.map +1 -0
  131. package/lib/typescript/commonjs/components/index.d.ts +4 -0
  132. package/lib/typescript/commonjs/components/index.d.ts.map +1 -1
  133. package/lib/typescript/commonjs/index.d.ts +8 -1
  134. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  135. package/lib/typescript/commonjs/notification-inbox.d.ts +5 -0
  136. package/lib/typescript/commonjs/notification-inbox.d.ts.map +1 -1
  137. package/lib/typescript/commonjs/specs/components/NotificationInboxBellNativeComponent.d.ts +13 -0
  138. package/lib/typescript/commonjs/specs/components/NotificationInboxBellNativeComponent.d.ts.map +1 -0
  139. package/lib/typescript/commonjs/specs/components/NotificationInboxViewNativeComponent.d.ts +12 -0
  140. package/lib/typescript/commonjs/specs/components/NotificationInboxViewNativeComponent.d.ts.map +1 -0
  141. package/lib/typescript/commonjs/specs/modules/NativeZixflow.d.ts +6 -1
  142. package/lib/typescript/commonjs/specs/modules/NativeZixflow.d.ts.map +1 -1
  143. package/lib/typescript/commonjs/specs/modules/NativeZixflowGeofence.d.ts +13 -0
  144. package/lib/typescript/commonjs/specs/modules/NativeZixflowGeofence.d.ts.map +1 -0
  145. package/lib/typescript/commonjs/specs/modules/NativeZixflowLiveActivities.d.ts +31 -0
  146. package/lib/typescript/commonjs/specs/modules/NativeZixflowLiveActivities.d.ts.map +1 -0
  147. package/lib/typescript/commonjs/specs/modules/NativeZixflowMessagingInApp.d.ts +5 -1
  148. package/lib/typescript/commonjs/specs/modules/NativeZixflowMessagingInApp.d.ts.map +1 -1
  149. package/lib/typescript/commonjs/specs/modules/NativeZixflowMessagingPush.d.ts +1 -1
  150. package/lib/typescript/commonjs/specs/modules/NativeZixflowMessagingPush.d.ts.map +1 -1
  151. package/lib/typescript/commonjs/types/data-pipelines.d.ts +53 -1
  152. package/lib/typescript/commonjs/types/data-pipelines.d.ts.map +1 -1
  153. package/lib/typescript/commonjs/types/in-app.d.ts +22 -0
  154. package/lib/typescript/commonjs/types/in-app.d.ts.map +1 -1
  155. package/lib/typescript/commonjs/types/inbox.d.ts +97 -0
  156. package/lib/typescript/commonjs/types/inbox.d.ts.map +1 -0
  157. package/lib/typescript/commonjs/types/index.d.ts +2 -0
  158. package/lib/typescript/commonjs/types/index.d.ts.map +1 -1
  159. package/lib/typescript/commonjs/types/live-activities.d.ts +161 -0
  160. package/lib/typescript/commonjs/types/live-activities.d.ts.map +1 -0
  161. package/lib/typescript/commonjs/utils/param-validation.d.ts +7 -0
  162. package/lib/typescript/commonjs/utils/param-validation.d.ts.map +1 -1
  163. package/lib/typescript/commonjs/zixflow-cdp.d.ts +38 -0
  164. package/lib/typescript/commonjs/zixflow-cdp.d.ts.map +1 -1
  165. package/lib/typescript/commonjs/zixflow-geofence.d.ts +36 -0
  166. package/lib/typescript/commonjs/zixflow-geofence.d.ts.map +1 -0
  167. package/lib/typescript/commonjs/zixflow-inapp.d.ts +36 -2
  168. package/lib/typescript/commonjs/zixflow-inapp.d.ts.map +1 -1
  169. package/lib/typescript/commonjs/zixflow-liveactivities.d.ts +66 -0
  170. package/lib/typescript/commonjs/zixflow-liveactivities.d.ts.map +1 -0
  171. package/lib/typescript/module/components/NotificationInboxBellView.d.ts +39 -0
  172. package/lib/typescript/module/components/NotificationInboxBellView.d.ts.map +1 -0
  173. package/lib/typescript/module/components/NotificationInboxView.d.ts +25 -0
  174. package/lib/typescript/module/components/NotificationInboxView.d.ts.map +1 -0
  175. package/lib/typescript/module/components/index.d.ts +4 -0
  176. package/lib/typescript/module/components/index.d.ts.map +1 -1
  177. package/lib/typescript/module/index.d.ts +8 -1
  178. package/lib/typescript/module/index.d.ts.map +1 -1
  179. package/lib/typescript/module/notification-inbox.d.ts +5 -0
  180. package/lib/typescript/module/notification-inbox.d.ts.map +1 -1
  181. package/lib/typescript/module/specs/components/NotificationInboxBellNativeComponent.d.ts +13 -0
  182. package/lib/typescript/module/specs/components/NotificationInboxBellNativeComponent.d.ts.map +1 -0
  183. package/lib/typescript/module/specs/components/NotificationInboxViewNativeComponent.d.ts +12 -0
  184. package/lib/typescript/module/specs/components/NotificationInboxViewNativeComponent.d.ts.map +1 -0
  185. package/lib/typescript/module/specs/modules/NativeZixflow.d.ts +6 -1
  186. package/lib/typescript/module/specs/modules/NativeZixflow.d.ts.map +1 -1
  187. package/lib/typescript/module/specs/modules/NativeZixflowGeofence.d.ts +13 -0
  188. package/lib/typescript/module/specs/modules/NativeZixflowGeofence.d.ts.map +1 -0
  189. package/lib/typescript/module/specs/modules/NativeZixflowLiveActivities.d.ts +31 -0
  190. package/lib/typescript/module/specs/modules/NativeZixflowLiveActivities.d.ts.map +1 -0
  191. package/lib/typescript/module/specs/modules/NativeZixflowMessagingInApp.d.ts +5 -1
  192. package/lib/typescript/module/specs/modules/NativeZixflowMessagingInApp.d.ts.map +1 -1
  193. package/lib/typescript/module/specs/modules/NativeZixflowMessagingPush.d.ts +1 -1
  194. package/lib/typescript/module/specs/modules/NativeZixflowMessagingPush.d.ts.map +1 -1
  195. package/lib/typescript/module/types/data-pipelines.d.ts +53 -1
  196. package/lib/typescript/module/types/data-pipelines.d.ts.map +1 -1
  197. package/lib/typescript/module/types/in-app.d.ts +22 -0
  198. package/lib/typescript/module/types/in-app.d.ts.map +1 -1
  199. package/lib/typescript/module/types/inbox.d.ts +97 -0
  200. package/lib/typescript/module/types/inbox.d.ts.map +1 -0
  201. package/lib/typescript/module/types/index.d.ts +2 -0
  202. package/lib/typescript/module/types/index.d.ts.map +1 -1
  203. package/lib/typescript/module/types/live-activities.d.ts +161 -0
  204. package/lib/typescript/module/types/live-activities.d.ts.map +1 -0
  205. package/lib/typescript/module/utils/param-validation.d.ts +7 -0
  206. package/lib/typescript/module/utils/param-validation.d.ts.map +1 -1
  207. package/lib/typescript/module/zixflow-cdp.d.ts +38 -0
  208. package/lib/typescript/module/zixflow-cdp.d.ts.map +1 -1
  209. package/lib/typescript/module/zixflow-geofence.d.ts +36 -0
  210. package/lib/typescript/module/zixflow-geofence.d.ts.map +1 -0
  211. package/lib/typescript/module/zixflow-inapp.d.ts +36 -2
  212. package/lib/typescript/module/zixflow-inapp.d.ts.map +1 -1
  213. package/lib/typescript/module/zixflow-liveactivities.d.ts +66 -0
  214. package/lib/typescript/module/zixflow-liveactivities.d.ts.map +1 -0
  215. package/package.json +11 -2
  216. package/src/components/NotificationInboxBellView.tsx +55 -0
  217. package/src/components/NotificationInboxView.tsx +30 -0
  218. package/src/components/index.ts +6 -0
  219. package/src/index.ts +8 -3
  220. package/src/notification-inbox.ts +1 -1
  221. package/src/specs/components/NotificationInboxBellNativeComponent.ts +24 -0
  222. package/src/specs/components/NotificationInboxViewNativeComponent.ts +22 -0
  223. package/src/specs/modules/NativeZixflow.ts +9 -1
  224. package/src/specs/modules/NativeZixflowGeofence.ts +14 -0
  225. package/src/specs/modules/NativeZixflowLiveActivities.ts +39 -0
  226. package/src/specs/modules/NativeZixflowMessagingInApp.ts +12 -1
  227. package/src/specs/modules/NativeZixflowMessagingPush.ts +3 -1
  228. package/src/types/data-pipelines.ts +54 -1
  229. package/src/types/in-app.ts +23 -0
  230. package/src/types/inbox.ts +110 -0
  231. package/src/types/index.ts +2 -0
  232. package/src/types/live-activities.ts +170 -0
  233. package/src/utils/param-validation.ts +103 -0
  234. package/src/zixflow-cdp.ts +117 -1
  235. package/src/zixflow-geofence.ts +56 -0
  236. package/src/zixflow-inapp.ts +125 -2
  237. package/src/zixflow-liveactivities.ts +86 -0
  238. package/zixflow-reactnative-richpush.podspec +4 -1
  239. package/zixflow-reactnative.podspec +26 -0
@@ -2,14 +2,102 @@ import ZixflowAnalytics
2
2
  import ZixflowDataPipelines
3
3
  import ZixflowInternalCommon
4
4
  import ZixflowMessagingInApp
5
+ import Foundation
5
6
 
6
7
  @objc(NativeZixflow)
7
8
  public class NativeZixflow: NSObject {
8
9
  private let logger: ZixflowInternalCommon.Logger = DIGraphShared.shared.logger
10
+ private weak var objcEventEmitter: AnyObject?
11
+ private var deepLinkHandlerToken: UUID?
9
12
  /// Checks whether the Zixflow SDK has been initialized.
10
13
  /// Returns `true` if the SDK has been successfully initialized, `false` otherwise.
11
14
  private var isInitialized: Bool { Zixflow.shared.implementation != nil }
12
15
 
16
+ /// Installs React Native scene deep-link routing before the JavaScript bridge starts.
17
+ /// Call this at the start of `scene(_:willConnectTo:options:)` in a UIScene host.
18
+ @objc
19
+ public static func configureSceneDeepLinkRouting() {
20
+ // A natively initialized host owns its callback. Scene connection happens after
21
+ // application launch, so never replace a callback that native initialization installed.
22
+ guard Zixflow.shared.implementation == nil else { return }
23
+ ZixflowReactNativeDeepLinkRouter.install()
24
+ }
25
+
26
+ /// Installs scene deep-link routing for the acknowledged JavaScript handler before the React
27
+ /// Native bridge starts. Call this at the start of `scene(_:willConnectTo:options:)` when the
28
+ /// host uses `Zixflow.setDeepLinkHandler`.
29
+ @objc
30
+ public static func configureAcknowledgedSceneDeepLinkRouting() {
31
+ // The cold scene connection happens before JavaScript can register its handler. Record the
32
+ // host's ownership choice now so initialization cannot drain a buffered URL into Linking.
33
+ guard Zixflow.shared.implementation == nil else { return }
34
+ ZixflowReactNativeDeepLinkRouter.installAcknowledgedHandler()
35
+ }
36
+
37
+ /// Installs deep-link routing for an Expo-owned scene lifecycle.
38
+ ///
39
+ /// Expo forwards scene URLs into React Native Linking itself, including on React Native
40
+ /// versions that do not expose the scene Linking selector used by the generic integration.
41
+ @objc
42
+ public static func configureExpoSceneDeepLinkRouting() {
43
+ guard Zixflow.shared.implementation == nil else { return }
44
+ // Native initialization preserves an existing callback when its config does not provide one.
45
+ ZixflowReactNativeDeepLinkRouter.installForExpoSceneLifecycle()
46
+ }
47
+
48
+ /// Marks the React Native Linking listener as ready for buffered scene URLs.
49
+ @objc
50
+ func setDeepLinkRoutingReady() {
51
+ ZixflowReactNativeDeepLinkRouter.markReactNativeReady()
52
+ }
53
+
54
+ @objc
55
+ public func setEventEmitter(_ emitter: AnyObject) {
56
+ objcEventEmitter = emitter
57
+ }
58
+
59
+ @objc
60
+ func registerDeepLinkHandler() {
61
+ guard objcEventEmitter != nil else {
62
+ logger.error("Zixflow could not register the React Native deep-link handler")
63
+ return
64
+ }
65
+
66
+ deepLinkHandlerToken = ZixflowReactNativeDeepLinkRouter.registerHandler { [weak self] id, url in
67
+ self?.sendDeepLink(id: id, url: url)
68
+ }
69
+ }
70
+
71
+ @objc
72
+ func unregisterDeepLinkHandler() {
73
+ guard let token = deepLinkHandlerToken else { return }
74
+ ZixflowReactNativeDeepLinkRouter.unregisterHandler(token)
75
+ deepLinkHandlerToken = nil
76
+ }
77
+
78
+ @objc
79
+ func acknowledgeDeepLink(_ id: String, handled: Bool) {
80
+ ZixflowReactNativeDeepLinkRouter.acknowledge(id, handled: handled)
81
+ }
82
+
83
+ @objc
84
+ func invalidate() {
85
+ unregisterDeepLinkHandler()
86
+ }
87
+
88
+ private func sendDeepLink(id: String, url: String) {
89
+ guard let emitter = objcEventEmitter else { return }
90
+
91
+ // Codegen derives this selector from `onDeepLinkReceived` in NativeZixflow.ts. Keep it
92
+ // in sync with that spec property and the forwarding override in NativeZixflow.mm.
93
+ let selector = Selector(("emitOnDeepLinkReceived:"))
94
+ guard emitter.responds(to: selector) else {
95
+ logger.error("Zixflow could not emit a React Native deep-link event")
96
+ return
97
+ }
98
+ _ = emitter.perform(selector, with: ["id": id, "url": url] as NSDictionary)
99
+ }
100
+
13
101
  /// Ensures that the Zixflow SDK is initialized before performing operations.
14
102
  /// Logs an error and returns false if the SDK is not initialized.
15
103
  private func ensureInitialized() -> Bool {
@@ -30,6 +118,7 @@ public class NativeZixflow: NSObject {
30
118
  // Skip initialization if already initialized
31
119
  if isInitialized {
32
120
  logger.info("Zixflow SDK is already initialized. Skipping initialization.")
121
+ ZixflowReactNativeDeepLinkRouter.markReactNativeReady()
33
122
  resolve(true)
34
123
  return
35
124
  }
@@ -48,12 +137,56 @@ public class NativeZixflow: NSObject {
48
137
 
49
138
  let sdkConfigBuilder = try SDKConfigBuilder.create(from: config)
50
139
 
140
+ let shouldInstallSceneRouter =
141
+ ZixflowReactNativeDeepLinkRouter.isSceneLifecycleEnabled ||
142
+ (packageSource == "Expo" && ZixflowReactNativeDeepLinkRouter.isExpoSceneLifecycleEnabled)
143
+ if shouldInstallSceneRouter {
144
+ _ = sdkConfigBuilder.deepLinkCallback { url in
145
+ ZixflowReactNativeDeepLinkRouter.accept(url)
146
+ return true // React Native Linking cannot report handling; JavaScript owns fallback.
147
+ }
148
+ }
149
+
51
150
  #if ZIXFLOW_LOCATION_ENABLED
52
- if let locationModule = NativeLocation.module(from: config) {
151
+ // Geofence implies location: register the location module whenever location
152
+ // config is provided or geofence is enabled, since geofence relies on the
153
+ // location module's fixes.
154
+ #if ZIXFLOW_GEOFENCE_ENABLED
155
+ // Match NativeGeofence.module(from:): only a dictionary counts as configured,
156
+ // so a non-map value (e.g. null bridged from JS) doesn't imply location.
157
+ let geofenceConfigured = config["geofence"] as? [String: Any] != nil
158
+ #else
159
+ let geofenceConfigured = false
160
+ #endif
161
+ if let locationModule = NativeLocation.module(from: config, geofenceEnabled: geofenceConfigured) {
53
162
  _ = sdkConfigBuilder.addModule(locationModule)
54
163
  }
55
164
  #endif
56
165
 
166
+ #if ZIXFLOW_GEOFENCE_ENABLED
167
+ // Geofence runs automatically once registered; relies on the location module above.
168
+ if let geofenceModule = NativeGeofence.module(from: config) {
169
+ _ = sdkConfigBuilder.addModule(geofenceModule)
170
+ }
171
+ #endif
172
+
173
+ #if ZIXFLOW_LIVEACTIVITIES_ENABLED
174
+ if let liveActivitiesModule = NativeLiveActivities.module(from: config) {
175
+ _ = sdkConfigBuilder.addModule(liveActivitiesModule)
176
+ }
177
+ #endif
178
+
179
+ // Customer value wins; default on when the geofence module is added, off otherwise.
180
+ #if ZIXFLOW_GEOFENCE_ENABLED
181
+ // Match NativeGeofence.module(from:): only a dictionary adds the module, so
182
+ // background delivery shouldn't default on for a non-map geofence value.
183
+ let geofenceAdded = config["geofence"] as? [String: Any] != nil
184
+ #else
185
+ let geofenceAdded = false
186
+ #endif
187
+ let allowBackgroundDelivery = (config["ios"] as? [String: Any])?["allowBackgroundDelivery"] as? Bool
188
+ _ = sdkConfigBuilder.allowBackgroundDelivery(allowBackgroundDelivery ?? geofenceAdded)
189
+
57
190
  let builtConfig = sdkConfigBuilder.build()
58
191
 
59
192
  // Only Zixflow.initialize must run on the main thread (e.g. for Location module).
@@ -77,6 +210,7 @@ public class NativeZixflow: NSObject {
77
210
  self.logger.debug(
78
211
  "Zixflow SDK (\(packageSource ?? "") \(packageVersion ?? "")) initialized with config: \(config)"
79
212
  )
213
+ ZixflowReactNativeDeepLinkRouter.markReactNativeReady()
80
214
  resolve(true)
81
215
  }
82
216
  } catch {
@@ -0,0 +1,123 @@
1
+ import Foundation
2
+
3
+ struct ZixflowReactNativeDeepLinkRequestStore {
4
+ enum Acceptance: Equatable {
5
+ case buffered(UUID)
6
+ case linking(URL)
7
+ case handler(UUID, URL)
8
+ }
9
+
10
+ enum Resolution: Equatable {
11
+ case handled
12
+ case fallback(URL)
13
+ }
14
+
15
+ private enum DeliveryMode: Equatable {
16
+ case unavailable
17
+ case linking
18
+ case handler
19
+ }
20
+
21
+ private enum RequestState: Equatable {
22
+ case buffered
23
+ case awaitingAcknowledgement
24
+ }
25
+
26
+ private struct Request {
27
+ let id: UUID
28
+ let url: URL
29
+ var state: RequestState
30
+ }
31
+
32
+ private var isLinkingReady = false
33
+ private var requiresHandler = false
34
+ private var hasHandler = false
35
+ private var requests: [Request] = []
36
+
37
+ private var deliveryMode: DeliveryMode {
38
+ if hasHandler {
39
+ return .handler
40
+ }
41
+ if !requiresHandler, isLinkingReady {
42
+ return .linking
43
+ }
44
+ return .unavailable
45
+ }
46
+
47
+ var requiresAcknowledgedHandler: Bool {
48
+ requiresHandler
49
+ }
50
+
51
+ var canDeliverWithLinking: Bool {
52
+ isLinkingReady
53
+ }
54
+
55
+ mutating func requireAcknowledgedHandler() {
56
+ requiresHandler = true
57
+ }
58
+
59
+ mutating func accept(_ url: URL) -> Acceptance {
60
+ switch deliveryMode {
61
+ case .unavailable:
62
+ let request = Request(id: UUID(), url: url, state: .buffered)
63
+ requests.append(request)
64
+ return .buffered(request.id)
65
+ case .linking:
66
+ return .linking(url)
67
+ case .handler:
68
+ let request = Request(id: UUID(), url: url, state: .awaitingAcknowledgement)
69
+ requests.append(request)
70
+ return .handler(request.id, request.url)
71
+ }
72
+ }
73
+
74
+ mutating func useLinking() -> [URL] {
75
+ isLinkingReady = true
76
+ guard deliveryMode == .linking else { return [] }
77
+
78
+ let urls = requests.compactMap { request in
79
+ request.state == .buffered ? request.url : nil
80
+ }
81
+ requests.removeAll { $0.state == .buffered }
82
+ return urls
83
+ }
84
+
85
+ mutating func useHandler() -> [(UUID, URL)] {
86
+ hasHandler = true
87
+ var deliveries: [(UUID, URL)] = []
88
+ for index in requests.indices where requests[index].state == .buffered {
89
+ requests[index].state = .awaitingAcknowledgement
90
+ deliveries.append((requests[index].id, requests[index].url))
91
+ }
92
+ return deliveries
93
+ }
94
+
95
+ mutating func removeHandler() {
96
+ hasHandler = false
97
+ }
98
+
99
+ mutating func acknowledge(_ id: UUID, handled: Bool) -> Resolution? {
100
+ guard let index = requests.firstIndex(where: {
101
+ $0.id == id && $0.state == .awaitingAcknowledgement
102
+ }) else { return nil }
103
+
104
+ let url = requests.remove(at: index).url
105
+ return handled ? .handled : .fallback(url)
106
+ }
107
+
108
+ mutating func expireReadiness(_ id: UUID) -> URL? {
109
+ expire(id, expectedState: .buffered)
110
+ }
111
+
112
+ mutating func expireAcknowledgement(_ id: UUID) -> URL? {
113
+ expire(id, expectedState: .awaitingAcknowledgement)
114
+ }
115
+
116
+ private mutating func expire(_ id: UUID, expectedState: RequestState) -> URL? {
117
+ guard let index = requests.firstIndex(where: {
118
+ $0.id == id && $0.state == expectedState
119
+ }) else { return nil }
120
+
121
+ return requests.remove(at: index).url
122
+ }
123
+ }
@@ -0,0 +1,312 @@
1
+ @_spi(Internal) import ZixflowInternalCommon
2
+ import Foundation
3
+ import UIKit
4
+
5
+ enum ZixflowReactNativeDeepLinkRouter {
6
+ private static let readinessTimeout: TimeInterval = 10
7
+ private static let acknowledgementTimeout: TimeInterval = 10
8
+ private static let sceneManifestKey = "UIApplicationSceneManifest"
9
+ private static let sceneConfigurationsKey = "UISceneConfigurations"
10
+ private static let sceneOpenURLContextsSelector = NSSelectorFromString("scene:openURLContexts:")
11
+ // React Native 0.83.6 and 0.88.0-nightly-20260823-0c7f63a4e use this
12
+ // notification name and payload for Linking URL events.
13
+ private static let openURLNotification = Notification.Name("RCTOpenURLNotification")
14
+ private static let stateLock = NSLock()
15
+ private static var requestStore = ZixflowReactNativeDeepLinkRequestStore()
16
+ private static var handlerEmitter: ((_ id: String, _ url: String) -> Void)?
17
+ private static var handlerToken: UUID?
18
+
19
+ static var requiresAcknowledgedHandler: Bool {
20
+ stateLock.lock()
21
+ defer { stateLock.unlock() }
22
+ return requestStore.requiresAcknowledgedHandler
23
+ }
24
+
25
+ private static var hasSceneManifest: Bool {
26
+ guard let manifest = Bundle.main.object(forInfoDictionaryKey: sceneManifestKey) as? [String: Any],
27
+ let configurations = manifest[sceneConfigurationsKey] as? [String: Any]
28
+ else { return false }
29
+
30
+ return !configurations.isEmpty
31
+ }
32
+
33
+ /// Mirrors `RCTIsSceneDelegateApp()` without linking to the internal symbol, and also requires
34
+ /// React Native's scene-Linking entry point. A scene manifest alone does not make older React
35
+ /// Native versions scene-aware.
36
+ static var isSceneLifecycleEnabled: Bool {
37
+ guard hasSceneManifest,
38
+ let linkingManager = NSClassFromString("RCTLinkingManager")
39
+ else { return false }
40
+
41
+ return linkingManager.responds(to: sceneOpenURLContextsSelector)
42
+ }
43
+
44
+ /// Expo owns scene-to-Linking forwarding even when its React Native version does not expose
45
+ /// the scene selector itself.
46
+ static var isExpoSceneLifecycleEnabled: Bool {
47
+ hasSceneManifest && NSClassFromString("EXExpoAppSceneDelegate") != nil
48
+ }
49
+
50
+ static func install() {
51
+ guard isSceneLifecycleEnabled else { return }
52
+ installCallback(requiringAcknowledgedHandler: false)
53
+ }
54
+
55
+ static func installAcknowledgedHandler() {
56
+ guard isSceneLifecycleEnabled else {
57
+ DIGraphShared.shared.logger.error(
58
+ "Zixflow could not install acknowledged deep-link routing because the host " +
59
+ "does not expose a supported React Native scene lifecycle"
60
+ )
61
+ return
62
+ }
63
+ installCallback(requiringAcknowledgedHandler: true)
64
+ }
65
+
66
+ /// Expo owns scene-to-Linking forwarding even on React Native versions that do not expose the
67
+ /// scene selector themselves. Prefer the generic React Native capability when available, then
68
+ /// fall back to Expo's scene delegate capability.
69
+ static func installForExpoSceneLifecycle() {
70
+ guard isSceneLifecycleEnabled || isExpoSceneLifecycleEnabled else {
71
+ DIGraphShared.shared.logger.error(
72
+ "Zixflow could not install Expo scene deep-link routing because the host " +
73
+ "does not expose a supported React Native or Expo scene lifecycle"
74
+ )
75
+ return
76
+ }
77
+ installCallback(requiringAcknowledgedHandler: false)
78
+ }
79
+
80
+ private static func installCallback(requiringAcknowledgedHandler: Bool) {
81
+ stateLock.lock()
82
+ if requiringAcknowledgedHandler {
83
+ requestStore.requireAcknowledgedHandler()
84
+ }
85
+ stateLock.unlock()
86
+ DIGraphShared.shared.deepLinkUtil.setDeepLinkCallback { url in
87
+ accept(url)
88
+ return true
89
+ }
90
+ }
91
+
92
+ static func accept(_ url: URL) {
93
+ stateLock.lock()
94
+ let acceptance = requestStore.accept(url)
95
+ stateLock.unlock()
96
+
97
+ switch acceptance {
98
+ case let .buffered(id):
99
+ DIGraphShared.shared.logger.info(
100
+ "Zixflow buffered an SDK deep link until a React Native route is ready"
101
+ )
102
+ scheduleReadinessExpiration(for: id)
103
+ case let .linking(url):
104
+ route(url)
105
+ case let .handler(id, url):
106
+ publishToHandler(id: id, url: url)
107
+ }
108
+ }
109
+
110
+ static func markReactNativeReady() {
111
+ let markReady = {
112
+ stateLock.lock()
113
+ let urls = requestStore.useLinking()
114
+ stateLock.unlock()
115
+
116
+ urls.forEach(route)
117
+ }
118
+ if Thread.isMainThread {
119
+ markReady()
120
+ } else {
121
+ DispatchQueue.main.async(execute: markReady)
122
+ }
123
+ }
124
+
125
+ static func registerHandler(
126
+ _ emitter: @escaping (_ id: String, _ url: String) -> Void
127
+ ) -> UUID {
128
+ let token = UUID()
129
+ stateLock.lock()
130
+ let hasAcknowledgedHandlerConfiguration = requestStore.requiresAcknowledgedHandler
131
+ handlerToken = token
132
+ handlerEmitter = emitter
133
+ let deliveries = requestStore.useHandler()
134
+ stateLock.unlock()
135
+
136
+ if !hasAcknowledgedHandlerConfiguration {
137
+ DIGraphShared.shared.logger.error(
138
+ "Zixflow registered an acknowledged deep-link handler without configuring " +
139
+ "acknowledged scene routing before React Native started"
140
+ )
141
+ }
142
+ for (id, url) in deliveries {
143
+ publishToHandler(id: id, url: url)
144
+ }
145
+ return token
146
+ }
147
+
148
+ static func unregisterHandler(_ token: UUID) {
149
+ stateLock.lock()
150
+ guard handlerToken == token else {
151
+ stateLock.unlock()
152
+ return
153
+ }
154
+ handlerToken = nil
155
+ handlerEmitter = nil
156
+ requestStore.removeHandler()
157
+ stateLock.unlock()
158
+ }
159
+
160
+ static func acknowledge(_ id: String, handled: Bool) {
161
+ guard let requestId = UUID(uuidString: id) else {
162
+ DIGraphShared.shared.logger.error(
163
+ "Zixflow ignored an invalid React Native deep-link acknowledgement"
164
+ )
165
+ return
166
+ }
167
+
168
+ stateLock.lock()
169
+ let resolution = requestStore.acknowledge(requestId, handled: handled)
170
+ stateLock.unlock()
171
+
172
+ switch resolution {
173
+ case .handled:
174
+ DIGraphShared.shared.logger.info(
175
+ "React Native acknowledged the Zixflow deep link"
176
+ )
177
+ case let .fallback(url):
178
+ DIGraphShared.shared.logger.info(
179
+ "React Native declined the Zixflow deep link; using the native fallback"
180
+ )
181
+ fallback(url)
182
+ case nil:
183
+ DIGraphShared.shared.logger.info(
184
+ "Zixflow ignored a late or unknown React Native deep-link acknowledgement; " +
185
+ "the native fallback may have already routed the destination"
186
+ )
187
+ }
188
+ }
189
+
190
+ static func route(_ url: URL) {
191
+ let publish = {
192
+ // React Native's AppDelegate URL entry point rejects scene-based hosts. Linking listens
193
+ // for this notification in both lifecycle modes, so publish the URL without choosing a scene.
194
+ DIGraphShared.shared.logger.info(
195
+ "Zixflow published an SDK deep link to React Native Linking"
196
+ )
197
+ NotificationCenter.default.post(
198
+ name: openURLNotification,
199
+ object: nil,
200
+ userInfo: ["url": url.absoluteString]
201
+ )
202
+ }
203
+ if Thread.isMainThread {
204
+ publish()
205
+ } else {
206
+ DispatchQueue.main.async(execute: publish)
207
+ }
208
+ }
209
+
210
+ private static func publishToHandler(id: UUID, url: URL) {
211
+ let publish = {
212
+ stateLock.lock()
213
+ let emitter = handlerEmitter
214
+ stateLock.unlock()
215
+
216
+ emitter?(id.uuidString, url.absoluteString)
217
+ DispatchQueue.main.asyncAfter(deadline: .now() + acknowledgementTimeout) {
218
+ expireAcknowledgement(id)
219
+ }
220
+ }
221
+ if Thread.isMainThread {
222
+ publish()
223
+ } else {
224
+ DispatchQueue.main.async(execute: publish)
225
+ }
226
+ }
227
+
228
+ private static func scheduleReadinessExpiration(for id: UUID) {
229
+ DispatchQueue.main.asyncAfter(deadline: .now() + readinessTimeout) {
230
+ expireReadiness(id)
231
+ }
232
+ }
233
+
234
+ private static func expireReadiness(_ id: UUID) {
235
+ stateLock.lock()
236
+ let url = requestStore.expireReadiness(id)
237
+ stateLock.unlock()
238
+
239
+ guard let url else { return }
240
+
241
+ DIGraphShared.shared.logger.error(
242
+ "Zixflow is using the native fallback because React Native deep-link routing " +
243
+ "did not become ready in time"
244
+ )
245
+ fallback(url)
246
+ }
247
+
248
+ private static func expireAcknowledgement(_ id: UUID) {
249
+ stateLock.lock()
250
+ let url = requestStore.expireAcknowledgement(id)
251
+ stateLock.unlock()
252
+
253
+ guard let url else { return }
254
+
255
+ DIGraphShared.shared.logger.error(
256
+ "Zixflow is using the native fallback because the React Native deep-link " +
257
+ "handler did not acknowledge the URL in time"
258
+ )
259
+ fallback(url)
260
+ }
261
+
262
+ private static func fallback(_ url: URL) {
263
+ let open = {
264
+ let uiKit = DIGraphShared.shared.uIKitWrapper
265
+ if uiKit.continueNSUserActivity(webpageURL: url) {
266
+ DIGraphShared.shared.logger.info(
267
+ "Zixflow deep link was handled by the host AppDelegate fallback"
268
+ )
269
+ } else if isAppOwnedCustomScheme(url) {
270
+ stateLock.lock()
271
+ let linkingIsReady = requestStore.canDeliverWithLinking
272
+ stateLock.unlock()
273
+ route(url)
274
+ if linkingIsReady {
275
+ DIGraphShared.shared.logger.info(
276
+ "Zixflow forwarded the host app's custom-scheme deep link to React " +
277
+ "Native Linking"
278
+ )
279
+ } else {
280
+ DIGraphShared.shared.logger.error(
281
+ "Zixflow published the host app's custom-scheme deep link before " +
282
+ "React Native Linking was marked ready; the destination may not be handled"
283
+ )
284
+ }
285
+ } else {
286
+ uiKit.open(url: url)
287
+ DIGraphShared.shared.logger.info(
288
+ "Zixflow opened the deep link through the system fallback"
289
+ )
290
+ }
291
+ }
292
+ if Thread.isMainThread {
293
+ open()
294
+ } else {
295
+ DispatchQueue.main.async(execute: open)
296
+ }
297
+ }
298
+
299
+ private static func isAppOwnedCustomScheme(_ url: URL) -> Bool {
300
+ guard let scheme = url.scheme,
301
+ scheme.caseInsensitiveCompare("http") != .orderedSame,
302
+ scheme.caseInsensitiveCompare("https") != .orderedSame,
303
+ let urlTypes = Bundle.main.object(forInfoDictionaryKey: "CFBundleURLTypes")
304
+ as? [[String: Any]]
305
+ else { return false }
306
+
307
+ return urlTypes.contains { urlType in
308
+ guard let schemes = urlType["CFBundleURLSchemes"] as? [String] else { return false }
309
+ return schemes.contains { $0.caseInsensitiveCompare(scheme) == .orderedSame }
310
+ }
311
+ }
312
+ }
@@ -0,0 +1,46 @@
1
+ #import <React/RCTBridgeModule.h>
2
+ #import <RNZixflowSpec/RNZixflowSpec.h>
3
+
4
+ // Objective-C wrapper for new architecture TurboModule implementation
5
+ @interface RCTNativeZixflowGeofence : NSObject <NativeZixflowGeofenceSpec>
6
+ // Bridge to Swift implementation for cross-language compatibility
7
+ @property(nonatomic, strong) id<NativeZixflowGeofenceSpec> swiftBridge;
8
+ @end
9
+
10
+ @implementation RCTNativeZixflowGeofence
11
+
12
+ RCT_EXPORT_MODULE()
13
+
14
+ // Create TurboModule instance for new architecture JSI integration
15
+ - (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
16
+ (const facebook::react::ObjCTurboModule::InitParams &)params {
17
+ return std::make_shared<facebook::react::NativeZixflowGeofenceSpecJSI>(params);
18
+ }
19
+
20
+ - (instancetype)init {
21
+ if (self = [super init]) {
22
+ // Use runtime class lookup - NativeGeofence class only exists when the ZixflowLocationGeofence subspec is installed
23
+ Class swiftClass = NSClassFromString(@"NativeZixflowGeofence");
24
+ if (swiftClass) {
25
+ _swiftBridge = [[swiftClass alloc] init];
26
+ }
27
+ }
28
+ return self;
29
+ }
30
+
31
+ // Module initialization can happen on background thread
32
+ + (BOOL)requiresMainQueueSetup {
33
+ return NO;
34
+ }
35
+
36
+ - (void)refreshFromCurrentLocation {
37
+ if (!_swiftBridge) return;
38
+ [_swiftBridge refreshFromCurrentLocation];
39
+ }
40
+
41
+ // Export class factory function for React Native component registration
42
+ Class<RCTBridgeModule> NativeZixflowGeofenceCls(void) {
43
+ return RCTNativeZixflowGeofence.class;
44
+ }
45
+
46
+ @end
@@ -0,0 +1,27 @@
1
+ #if ZIXFLOW_GEOFENCE_ENABLED
2
+ import ZixflowDataPipelines
3
+ import ZixflowLocationGeofence
4
+
5
+ /// Registers the optional geofence module with the native iOS SDK and exposes its app-facing
6
+ /// methods. The reference to `GeofenceModule` is isolated here so it is only compiled when the
7
+ /// geofence subspec is installed. Geofence depends on the location module, which the caller
8
+ /// registers alongside it.
9
+ @objc(NativeZixflowGeofence)
10
+ public class NativeGeofence: NSObject {
11
+
12
+ /// Returns a `GeofenceModule` when the app opts into geofence via the `geofence` config,
13
+ /// applying the optional `locationMode` (defaults to `.automatic`).
14
+ static func module(from config: [String: Any]) -> GeofenceModule? {
15
+ guard let geofenceConfig = config["geofence"] as? [String: Any] else { return nil }
16
+ let locationModeValue = geofenceConfig["locationMode"] as? String
17
+ let locationMode: GeofenceLocationMode =
18
+ locationModeValue?.uppercased() == "MANUAL" ? .manual : .automatic
19
+ return GeofenceModule(config: GeofenceModuleConfig(locationMode: locationMode))
20
+ }
21
+
22
+ @objc
23
+ func refreshFromCurrentLocation() {
24
+ Zixflow.geofence.refreshFromCurrentLocation()
25
+ }
26
+ }
27
+ #endif