zixflow-reactnative 1.1.2 → 1.1.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 (249) hide show
  1. package/README.md +2224 -322
  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 +69 -24
  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 -3
  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/logging/NativeZixflowLoggingModule.kt +2 -2
  11. package/android/src/main/java/com/zixflow/reactnative/sdk/messaginginapp/NativeMessagingInAppModule.kt +149 -10
  12. package/android/src/main/java/com/zixflow/reactnative/sdk/messaginginapp/ReactInboxEventListener.kt +99 -0
  13. package/android/src/main/java/com/zixflow/reactnative/sdk/messaginginbox/NotificationInboxBellViewManager.kt +38 -0
  14. package/android/src/main/java/com/zixflow/reactnative/sdk/messaginginbox/NotificationInboxViewManager.kt +30 -0
  15. package/android/src/main/java/com/zixflow/reactnative/sdk/messaginginbox/ReactComposeHostView.kt +96 -0
  16. package/android/src/main/java/com/zixflow/reactnative/sdk/messaginginbox/ReactNotificationInboxBellView.kt +48 -0
  17. package/android/src/main/java/com/zixflow/reactnative/sdk/messaginginbox/ReactNotificationInboxView.kt +17 -0
  18. package/android/src/main/java/com/zixflow/reactnative/sdk/messagingpush/NativeMessagingPushModule.kt +11 -1
  19. package/android/src/main/res/values/zixflow_config.xml +1 -1
  20. package/android/zixflow-core.gradle +17 -7
  21. package/docs/cocoapods-deployment-target-normalization.md +139 -0
  22. package/ios/cocoapods_deployment_target.rb +432 -0
  23. package/ios/wrappers/NativeZixflow.mm +37 -0
  24. package/ios/wrappers/NativeZixflow.swift +135 -1
  25. package/ios/wrappers/ZixflowReactNativeDeepLinkRequestStore.swift +123 -0
  26. package/ios/wrappers/ZixflowReactNativeDeepLinkRouter.swift +312 -0
  27. package/ios/wrappers/geofence/NativeGeofence.mm +46 -0
  28. package/ios/wrappers/geofence/NativeGeofence.swift +27 -0
  29. package/ios/wrappers/inapp/NativeMessagingInApp.mm +22 -0
  30. package/ios/wrappers/inapp/NativeMessagingInApp.swift +90 -0
  31. package/ios/wrappers/inbox/InboxHostContainment.swift +40 -0
  32. package/ios/wrappers/inbox/RCTNotificationInboxBellNative.h +12 -0
  33. package/ios/wrappers/inbox/RCTNotificationInboxBellNative.mm +98 -0
  34. package/ios/wrappers/inbox/RCTNotificationInboxViewNative.h +12 -0
  35. package/ios/wrappers/inbox/RCTNotificationInboxViewNative.mm +78 -0
  36. package/ios/wrappers/inbox/ReactInboxEventListener.swift +76 -0
  37. package/ios/wrappers/inbox/ReactNotificationInboxBellView.h +22 -0
  38. package/ios/wrappers/inbox/ReactNotificationInboxBellView.swift +109 -0
  39. package/ios/wrappers/inbox/ReactNotificationInboxView.h +22 -0
  40. package/ios/wrappers/inbox/ReactNotificationInboxView.swift +69 -0
  41. package/ios/wrappers/liveactivities/NativeLiveActivities.mm +83 -0
  42. package/ios/wrappers/liveactivities/NativeLiveActivities.swift +390 -0
  43. package/ios/wrappers/location/NativeLocation.swift +7 -5
  44. package/ios/wrappers/utils/ZixflowConfigUtils.swift +5 -9
  45. package/ios/wrappers/utils/ZixflowConstants.swift +4 -0
  46. package/ios/wrappers/zixflow_reactnative.h +6 -0
  47. package/lib/commonjs/components/NotificationInboxBellView.js +49 -0
  48. package/lib/commonjs/components/NotificationInboxBellView.js.map +1 -0
  49. package/lib/commonjs/components/NotificationInboxView.js +36 -0
  50. package/lib/commonjs/components/NotificationInboxView.js.map +1 -0
  51. package/lib/commonjs/components/index.js +14 -0
  52. package/lib/commonjs/components/index.js.map +1 -1
  53. package/lib/commonjs/index.js +81 -4
  54. package/lib/commonjs/index.js.map +1 -1
  55. package/lib/commonjs/notification-inbox.js +1 -0
  56. package/lib/commonjs/notification-inbox.js.map +1 -1
  57. package/lib/commonjs/specs/components/NotificationInboxBellNativeComponent.ts +24 -0
  58. package/lib/commonjs/specs/components/NotificationInboxViewNativeComponent.ts +22 -0
  59. package/lib/commonjs/specs/modules/NativeZixflow.js.map +1 -1
  60. package/lib/commonjs/specs/modules/NativeZixflowGeofence.js +15 -0
  61. package/lib/commonjs/specs/modules/NativeZixflowGeofence.js.map +1 -0
  62. package/lib/commonjs/specs/modules/NativeZixflowLiveActivities.js +23 -0
  63. package/lib/commonjs/specs/modules/NativeZixflowLiveActivities.js.map +1 -0
  64. package/lib/commonjs/specs/modules/NativeZixflowMessagingInApp.js +3 -1
  65. package/lib/commonjs/specs/modules/NativeZixflowMessagingInApp.js.map +1 -1
  66. package/lib/commonjs/specs/modules/NativeZixflowMessagingPush.js +3 -1
  67. package/lib/commonjs/specs/modules/NativeZixflowMessagingPush.js.map +1 -1
  68. package/lib/commonjs/types/data-pipelines.js +15 -15
  69. package/lib/commonjs/types/data-pipelines.js.map +1 -1
  70. package/lib/commonjs/types/in-app.js +24 -1
  71. package/lib/commonjs/types/in-app.js.map +1 -1
  72. package/lib/commonjs/types/inbox.js +79 -0
  73. package/lib/commonjs/types/inbox.js.map +1 -0
  74. package/lib/commonjs/types/index.js +22 -0
  75. package/lib/commonjs/types/index.js.map +1 -1
  76. package/lib/commonjs/types/live-activities.js +85 -0
  77. package/lib/commonjs/types/live-activities.js.map +1 -0
  78. package/lib/commonjs/utils/param-validation.js +77 -2
  79. package/lib/commonjs/utils/param-validation.js.map +1 -1
  80. package/lib/commonjs/zixflow-cdp.js +101 -11
  81. package/lib/commonjs/zixflow-cdp.js.map +1 -1
  82. package/lib/commonjs/zixflow-geofence.js +55 -0
  83. package/lib/commonjs/zixflow-geofence.js.map +1 -0
  84. package/lib/commonjs/zixflow-inapp.js +120 -0
  85. package/lib/commonjs/zixflow-inapp.js.map +1 -1
  86. package/lib/commonjs/zixflow-liveactivities.js +85 -0
  87. package/lib/commonjs/zixflow-liveactivities.js.map +1 -0
  88. package/lib/commonjs/zixflow.js.map +1 -1
  89. package/lib/module/components/NotificationInboxBellView.js +44 -0
  90. package/lib/module/components/NotificationInboxBellView.js.map +1 -0
  91. package/lib/module/components/NotificationInboxView.js +31 -0
  92. package/lib/module/components/NotificationInboxView.js.map +1 -0
  93. package/lib/module/components/index.js +5 -0
  94. package/lib/module/components/index.js.map +1 -1
  95. package/lib/module/index.js +8 -3
  96. package/lib/module/index.js.map +1 -1
  97. package/lib/module/notification-inbox.js +1 -1
  98. package/lib/module/notification-inbox.js.map +1 -1
  99. package/lib/module/specs/components/NotificationInboxBellNativeComponent.ts +24 -0
  100. package/lib/module/specs/components/NotificationInboxViewNativeComponent.ts +22 -0
  101. package/lib/module/specs/modules/NativeZixflow.js.map +1 -1
  102. package/lib/module/specs/modules/NativeZixflowGeofence.js +13 -0
  103. package/lib/module/specs/modules/NativeZixflowGeofence.js.map +1 -0
  104. package/lib/module/specs/modules/NativeZixflowLiveActivities.js +21 -0
  105. package/lib/module/specs/modules/NativeZixflowLiveActivities.js.map +1 -0
  106. package/lib/module/specs/modules/NativeZixflowMessagingInApp.js +3 -1
  107. package/lib/module/specs/modules/NativeZixflowMessagingInApp.js.map +1 -1
  108. package/lib/module/specs/modules/NativeZixflowMessagingPush.js +3 -1
  109. package/lib/module/specs/modules/NativeZixflowMessagingPush.js.map +1 -1
  110. package/lib/module/types/data-pipelines.js +15 -15
  111. package/lib/module/types/data-pipelines.js.map +1 -1
  112. package/lib/module/types/in-app.js +24 -0
  113. package/lib/module/types/in-app.js.map +1 -1
  114. package/lib/module/types/inbox.js +75 -0
  115. package/lib/module/types/inbox.js.map +1 -0
  116. package/lib/module/types/index.js +2 -0
  117. package/lib/module/types/index.js.map +1 -1
  118. package/lib/module/types/live-activities.js +88 -0
  119. package/lib/module/types/live-activities.js.map +1 -0
  120. package/lib/module/utils/param-validation.js +77 -2
  121. package/lib/module/utils/param-validation.js.map +1 -1
  122. package/lib/module/zixflow-cdp.js +101 -11
  123. package/lib/module/zixflow-cdp.js.map +1 -1
  124. package/lib/module/zixflow-geofence.js +50 -0
  125. package/lib/module/zixflow-geofence.js.map +1 -0
  126. package/lib/module/zixflow-inapp.js +99 -1
  127. package/lib/module/zixflow-inapp.js.map +1 -1
  128. package/lib/module/zixflow-liveactivities.js +80 -0
  129. package/lib/module/zixflow-liveactivities.js.map +1 -0
  130. package/lib/module/zixflow.js +0 -6
  131. package/lib/module/zixflow.js.map +1 -1
  132. package/lib/typescript/commonjs/components/NotificationInboxBellView.d.ts +39 -0
  133. package/lib/typescript/commonjs/components/NotificationInboxBellView.d.ts.map +1 -0
  134. package/lib/typescript/commonjs/components/NotificationInboxView.d.ts +25 -0
  135. package/lib/typescript/commonjs/components/NotificationInboxView.d.ts.map +1 -0
  136. package/lib/typescript/commonjs/components/index.d.ts +4 -0
  137. package/lib/typescript/commonjs/components/index.d.ts.map +1 -1
  138. package/lib/typescript/commonjs/index.d.ts +8 -1
  139. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  140. package/lib/typescript/commonjs/notification-inbox.d.ts +5 -0
  141. package/lib/typescript/commonjs/notification-inbox.d.ts.map +1 -1
  142. package/lib/typescript/commonjs/specs/components/NotificationInboxBellNativeComponent.d.ts +13 -0
  143. package/lib/typescript/commonjs/specs/components/NotificationInboxBellNativeComponent.d.ts.map +1 -0
  144. package/lib/typescript/commonjs/specs/components/NotificationInboxViewNativeComponent.d.ts +12 -0
  145. package/lib/typescript/commonjs/specs/components/NotificationInboxViewNativeComponent.d.ts.map +1 -0
  146. package/lib/typescript/commonjs/specs/modules/NativeZixflow.d.ts +6 -1
  147. package/lib/typescript/commonjs/specs/modules/NativeZixflow.d.ts.map +1 -1
  148. package/lib/typescript/commonjs/specs/modules/NativeZixflowGeofence.d.ts +13 -0
  149. package/lib/typescript/commonjs/specs/modules/NativeZixflowGeofence.d.ts.map +1 -0
  150. package/lib/typescript/commonjs/specs/modules/NativeZixflowLiveActivities.d.ts +31 -0
  151. package/lib/typescript/commonjs/specs/modules/NativeZixflowLiveActivities.d.ts.map +1 -0
  152. package/lib/typescript/commonjs/specs/modules/NativeZixflowMessagingInApp.d.ts +5 -1
  153. package/lib/typescript/commonjs/specs/modules/NativeZixflowMessagingInApp.d.ts.map +1 -1
  154. package/lib/typescript/commonjs/specs/modules/NativeZixflowMessagingPush.d.ts +1 -1
  155. package/lib/typescript/commonjs/specs/modules/NativeZixflowMessagingPush.d.ts.map +1 -1
  156. package/lib/typescript/commonjs/types/data-pipelines.d.ts +54 -18
  157. package/lib/typescript/commonjs/types/data-pipelines.d.ts.map +1 -1
  158. package/lib/typescript/commonjs/types/in-app.d.ts +22 -0
  159. package/lib/typescript/commonjs/types/in-app.d.ts.map +1 -1
  160. package/lib/typescript/commonjs/types/inbox.d.ts +97 -0
  161. package/lib/typescript/commonjs/types/inbox.d.ts.map +1 -0
  162. package/lib/typescript/commonjs/types/index.d.ts +2 -0
  163. package/lib/typescript/commonjs/types/index.d.ts.map +1 -1
  164. package/lib/typescript/commonjs/types/live-activities.d.ts +161 -0
  165. package/lib/typescript/commonjs/types/live-activities.d.ts.map +1 -0
  166. package/lib/typescript/commonjs/utils/param-validation.d.ts +7 -0
  167. package/lib/typescript/commonjs/utils/param-validation.d.ts.map +1 -1
  168. package/lib/typescript/commonjs/zixflow-cdp.d.ts +38 -0
  169. package/lib/typescript/commonjs/zixflow-cdp.d.ts.map +1 -1
  170. package/lib/typescript/commonjs/zixflow-geofence.d.ts +36 -0
  171. package/lib/typescript/commonjs/zixflow-geofence.d.ts.map +1 -0
  172. package/lib/typescript/commonjs/zixflow-inapp.d.ts +36 -2
  173. package/lib/typescript/commonjs/zixflow-inapp.d.ts.map +1 -1
  174. package/lib/typescript/commonjs/zixflow-liveactivities.d.ts +66 -0
  175. package/lib/typescript/commonjs/zixflow-liveactivities.d.ts.map +1 -0
  176. package/lib/typescript/commonjs/zixflow.d.ts +0 -5
  177. package/lib/typescript/commonjs/zixflow.d.ts.map +1 -1
  178. package/lib/typescript/module/components/NotificationInboxBellView.d.ts +39 -0
  179. package/lib/typescript/module/components/NotificationInboxBellView.d.ts.map +1 -0
  180. package/lib/typescript/module/components/NotificationInboxView.d.ts +25 -0
  181. package/lib/typescript/module/components/NotificationInboxView.d.ts.map +1 -0
  182. package/lib/typescript/module/components/index.d.ts +4 -0
  183. package/lib/typescript/module/components/index.d.ts.map +1 -1
  184. package/lib/typescript/module/index.d.ts +8 -1
  185. package/lib/typescript/module/index.d.ts.map +1 -1
  186. package/lib/typescript/module/notification-inbox.d.ts +5 -0
  187. package/lib/typescript/module/notification-inbox.d.ts.map +1 -1
  188. package/lib/typescript/module/specs/components/NotificationInboxBellNativeComponent.d.ts +13 -0
  189. package/lib/typescript/module/specs/components/NotificationInboxBellNativeComponent.d.ts.map +1 -0
  190. package/lib/typescript/module/specs/components/NotificationInboxViewNativeComponent.d.ts +12 -0
  191. package/lib/typescript/module/specs/components/NotificationInboxViewNativeComponent.d.ts.map +1 -0
  192. package/lib/typescript/module/specs/modules/NativeZixflow.d.ts +6 -1
  193. package/lib/typescript/module/specs/modules/NativeZixflow.d.ts.map +1 -1
  194. package/lib/typescript/module/specs/modules/NativeZixflowGeofence.d.ts +13 -0
  195. package/lib/typescript/module/specs/modules/NativeZixflowGeofence.d.ts.map +1 -0
  196. package/lib/typescript/module/specs/modules/NativeZixflowLiveActivities.d.ts +31 -0
  197. package/lib/typescript/module/specs/modules/NativeZixflowLiveActivities.d.ts.map +1 -0
  198. package/lib/typescript/module/specs/modules/NativeZixflowMessagingInApp.d.ts +5 -1
  199. package/lib/typescript/module/specs/modules/NativeZixflowMessagingInApp.d.ts.map +1 -1
  200. package/lib/typescript/module/specs/modules/NativeZixflowMessagingPush.d.ts +1 -1
  201. package/lib/typescript/module/specs/modules/NativeZixflowMessagingPush.d.ts.map +1 -1
  202. package/lib/typescript/module/types/data-pipelines.d.ts +54 -18
  203. package/lib/typescript/module/types/data-pipelines.d.ts.map +1 -1
  204. package/lib/typescript/module/types/in-app.d.ts +22 -0
  205. package/lib/typescript/module/types/in-app.d.ts.map +1 -1
  206. package/lib/typescript/module/types/inbox.d.ts +97 -0
  207. package/lib/typescript/module/types/inbox.d.ts.map +1 -0
  208. package/lib/typescript/module/types/index.d.ts +2 -0
  209. package/lib/typescript/module/types/index.d.ts.map +1 -1
  210. package/lib/typescript/module/types/live-activities.d.ts +161 -0
  211. package/lib/typescript/module/types/live-activities.d.ts.map +1 -0
  212. package/lib/typescript/module/utils/param-validation.d.ts +7 -0
  213. package/lib/typescript/module/utils/param-validation.d.ts.map +1 -1
  214. package/lib/typescript/module/zixflow-cdp.d.ts +38 -0
  215. package/lib/typescript/module/zixflow-cdp.d.ts.map +1 -1
  216. package/lib/typescript/module/zixflow-geofence.d.ts +36 -0
  217. package/lib/typescript/module/zixflow-geofence.d.ts.map +1 -0
  218. package/lib/typescript/module/zixflow-inapp.d.ts +36 -2
  219. package/lib/typescript/module/zixflow-inapp.d.ts.map +1 -1
  220. package/lib/typescript/module/zixflow-liveactivities.d.ts +66 -0
  221. package/lib/typescript/module/zixflow-liveactivities.d.ts.map +1 -0
  222. package/lib/typescript/module/zixflow.d.ts +0 -5
  223. package/lib/typescript/module/zixflow.d.ts.map +1 -1
  224. package/package.json +14 -5
  225. package/src/components/NotificationInboxBellView.tsx +55 -0
  226. package/src/components/NotificationInboxView.tsx +30 -0
  227. package/src/components/index.ts +6 -0
  228. package/src/index.ts +8 -3
  229. package/src/notification-inbox.ts +1 -1
  230. package/src/specs/components/NotificationInboxBellNativeComponent.ts +24 -0
  231. package/src/specs/components/NotificationInboxViewNativeComponent.ts +22 -0
  232. package/src/specs/modules/NativeZixflow.ts +9 -1
  233. package/src/specs/modules/NativeZixflowGeofence.ts +14 -0
  234. package/src/specs/modules/NativeZixflowLiveActivities.ts +39 -0
  235. package/src/specs/modules/NativeZixflowMessagingInApp.ts +12 -1
  236. package/src/specs/modules/NativeZixflowMessagingPush.ts +3 -1
  237. package/src/types/data-pipelines.ts +55 -19
  238. package/src/types/in-app.ts +23 -0
  239. package/src/types/inbox.ts +110 -0
  240. package/src/types/index.ts +2 -0
  241. package/src/types/live-activities.ts +170 -0
  242. package/src/utils/param-validation.ts +104 -1
  243. package/src/zixflow-cdp.ts +119 -13
  244. package/src/zixflow-geofence.ts +56 -0
  245. package/src/zixflow-inapp.ts +125 -2
  246. package/src/zixflow-liveactivities.ts +86 -0
  247. package/src/zixflow.ts +0 -6
  248. package/zixflow-reactnative-richpush.podspec +6 -3
  249. package/zixflow-reactnative.podspec +28 -2
@@ -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
@@ -66,11 +66,29 @@ RCT_EXPORT_MODULE(NativeZixflowMessagingInApp)
66
66
  [_swiftBridge dismissMessage];
67
67
  }
68
68
 
69
+ // Signature matches the Codegen protocol, which declares the argument nonnull. ObjC does not
70
+ // enforce that at runtime, so a JavaScript null still arrives here as nil and is forwarded; the
71
+ // Swift side takes an optional and reports it rather than trapping.
72
+ - (void)setColorScheme:(NSString *)colorScheme {
73
+ [self assertBridgeAvailable:@"during setColorScheme"];
74
+ [_swiftBridge setColorScheme:colorScheme];
75
+ }
76
+
69
77
  - (void)setupInboxListener {
70
78
  [self assertBridgeAvailable:@"during setupInboxListener"];
71
79
  [_swiftBridge setupInboxListener];
72
80
  }
73
81
 
82
+ - (void)registerInboxEventListener {
83
+ [self assertBridgeAvailable:@"during registerInboxEventListener"];
84
+ [_swiftBridge registerInboxEventListener];
85
+ }
86
+
87
+ - (void)unregisterInboxEventListener {
88
+ [self assertBridgeAvailable:@"during unregisterInboxEventListener"];
89
+ [_swiftBridge unregisterInboxEventListener];
90
+ }
91
+
74
92
  - (void)getMessages:(NSString *)topic
75
93
  resolve:(RCTPromiseResolveBlock)resolve
76
94
  reject:(RCTPromiseRejectBlock)reject {
@@ -110,6 +128,10 @@ RCT_EXPORT_MODULE(NativeZixflowMessagingInApp)
110
128
  [super emitOnInAppEventReceived:value];
111
129
  }
112
130
 
131
+ - (void)emitOnInboxEventReceived:(NSDictionary *)value {
132
+ [super emitOnInboxEventReceived:value];
133
+ }
134
+
113
135
  - (void)emitSubscribeToMessagesChanged:(NSDictionary *)value {
114
136
  [super emitSubscribeToMessagesChanged:value];
115
137
  }
@@ -50,6 +50,48 @@ public class NativeMessagingInApp: NSObject {
50
50
  // Clear in-app event listener to prevent leaks
51
51
  clearInAppEventListener()
52
52
  clearInboxChangeListener()
53
+ clearInboxEventListener()
54
+ }
55
+
56
+ // MARK: - Inbox Event Listener
57
+
58
+ /// Registers the native inbox event forwarder with the SDK and wires it to the ObjC event
59
+ /// emitter. Called by React Native when a JS inbox event listener is registered.
60
+ @objc(registerInboxEventListener)
61
+ public func registerInboxEventListener() {
62
+ ReactInboxEventListener.shared.setEventEmitter { [weak self] data in
63
+ guard let self else { return }
64
+ // Filter out nil values to convert [String: Any?] to [String: Any]
65
+ let body = data.compactMapValues { $0 }
66
+ self.sendInboxEvent(body: body)
67
+ }
68
+ MessagingInApp.shared.setInboxEventListener(ReactInboxEventListener.shared)
69
+ }
70
+
71
+ /// Unregisters the native inbox event forwarder from the SDK, restoring the SDK's default action
72
+ /// handling. Called by React Native when the JS inbox event listener is removed.
73
+ @objc(unregisterInboxEventListener)
74
+ public func unregisterInboxEventListener() {
75
+ MessagingInApp.shared.setInboxEventListener(nil)
76
+ ReactInboxEventListener.shared.clearEventEmitter()
77
+ }
78
+
79
+ private func clearInboxEventListener() {
80
+ MessagingInApp.shared.setInboxEventListener(nil)
81
+ ReactInboxEventListener.shared.clearEventEmitter()
82
+ }
83
+
84
+ // Send inbox event to React Native layer using ObjC event emitter
85
+ private func sendInboxEvent(body: [String: Any]) {
86
+ guard let emitter = objcEventEmitter else {
87
+ return
88
+ }
89
+
90
+ let selector = Selector(("emitOnInboxEventReceived:"))
91
+ guard emitter.responds(to: selector) else {
92
+ return
93
+ }
94
+ _ = emitter.perform(selector, with: body as NSDictionary)
53
95
  }
54
96
 
55
97
  /// Dismisses any currently displayed in-app message
@@ -58,6 +100,54 @@ public class NativeMessagingInApp: NSObject {
58
100
  MessagingInApp.shared.dismissMessage()
59
101
  }
60
102
 
103
+ /// Overrides the color scheme used to render in-app messages.
104
+ ///
105
+ /// Receives the raw value of the JavaScript `ZixflowColorScheme`, since the enum itself cannot
106
+ /// cross Codegen. An unrecognized value leaves the current scheme alone instead of resetting
107
+ /// it to `.auto`, so a typo cannot quietly undo a scheme the app set correctly earlier — the
108
+ /// Android bridge behaves the same way.
109
+ ///
110
+ /// The parameter is optional even though Codegen declares it non-null: untyped JavaScript can
111
+ /// still pass `null` or `undefined`, which the bridge forwards as `nil`. Bridging that into a
112
+ /// non-optional `String` would trap before the value could be reported, and Android accepts a
113
+ /// nullable argument for the same reason, so a non-optional type here would also leave the two
114
+ /// platforms behaving differently on the same input.
115
+ @objc(setColorScheme:)
116
+ public func setColorScheme(_ colorScheme: String?) {
117
+ guard let resolved = Self.colorScheme(fromRawValue: colorScheme) else {
118
+ logger.error(
119
+ "Unrecognized in-app colorScheme '\(colorScheme ?? "nil")', expected one of auto, light, dark. Leaving the color scheme unchanged."
120
+ )
121
+ return
122
+ }
123
+ // Without this the call is dropped in silence: `setColorScheme` forwards through the
124
+ // module's `implementation?`, which is nil until the SDK is initialized, and the only
125
+ // trace is an `.info` line the default `.error` log level discards. Android logs this
126
+ // case at error, so reporting it here is what keeps the two platforms diagnosable in the
127
+ // same way. Logged rather than failed, again to match Android, which completes the call.
128
+ guard MessagingInApp.shared.hasBeenInitialized else {
129
+ logger.error(
130
+ "In-app messaging is not available, so the color scheme was not applied. Ensure Zixflow SDK is initialized with the inApp configuration."
131
+ )
132
+ return
133
+ }
134
+ MessagingInApp.shared.setColorScheme(resolved)
135
+ }
136
+
137
+ /// Maps the wrapper's lowercase wire value onto the native `ColorScheme`.
138
+ ///
139
+ /// Matched explicitly rather than handed to `MessagingInAppConfigBuilder`, which resolves
140
+ /// anything unrecognized to `.auto`; here an unrecognized value has to stay distinguishable
141
+ /// so it can be reported.
142
+ private static func colorScheme(fromRawValue rawValue: String?) -> ColorScheme? {
143
+ switch rawValue {
144
+ case "auto": return .auto
145
+ case "light": return .light
146
+ case "dark": return .dark
147
+ default: return nil
148
+ }
149
+ }
150
+
61
151
  // MARK: - Inbox Methods
62
152
 
63
153
  @objc(setupInboxListener)
@@ -0,0 +1,40 @@
1
+ import Foundation
2
+ import UIKit
3
+
4
+ /// Shared view-controller containment for the inbox `UIHostingController` hosts.
5
+ ///
6
+ /// Adopting only a hosting controller's `view` leaves the controller outside the view-controller
7
+ /// hierarchy. UIKit then has no presentation context or trait/safe-area chain for it, which showed up
8
+ /// as a phantom top margin in the overlay's sheet (the sheet content resolved insets against the
9
+ /// wrong container) and means no lifecycle callbacks ever reach the hosted SwiftUI. Native usage does
10
+ /// not hit this because there the hosting controller *is* a real pushed view controller.
11
+ ///
12
+ /// Containment has to be deferred until the Fabric view is actually in a window: React Native creates
13
+ /// component views before mounting them, so at `init` there is no parent controller to attach to.
14
+ enum InboxHostContainment {
15
+ /// Nearest view controller up the responder chain, or nil while the view is unmounted.
16
+ static func nearestViewController(from view: UIView?) -> UIViewController? {
17
+ var responder: UIResponder? = view
18
+ while let current = responder {
19
+ if let viewController = current as? UIViewController {
20
+ return viewController
21
+ }
22
+ responder = current.next
23
+ }
24
+ return nil
25
+ }
26
+
27
+ /// Attaches `child` to the controller owning `view`. No-op when already attached or unmounted.
28
+ static func attach(_ child: UIViewController, hostedIn view: UIView?) {
29
+ guard child.parent == nil, let parent = nearestViewController(from: view) else { return }
30
+ parent.addChild(child)
31
+ child.didMove(toParent: parent)
32
+ }
33
+
34
+ /// Detaches `child` from its parent. No-op when it was never attached.
35
+ static func detach(_ child: UIViewController) {
36
+ guard child.parent != nil else { return }
37
+ child.willMove(toParent: nil)
38
+ child.removeFromParent()
39
+ }
40
+ }