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,432 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This packaged copy is owned by zixflow-reactnative and covered by its local test suite.
4
+
5
+ require "rubygems/version"
6
+
7
+ module Zixflow
8
+ # Normalizes every CocoaPods-generated target and every integrated application or extension
9
+ # target to a numeric iOS deployment target that the selected Xcode toolchain supports.
10
+ module CocoaPodsDeploymentTarget
11
+ BUILD_SETTING = "IPHONEOS_DEPLOYMENT_TARGET"
12
+ VERSION_PATTERN = /\A\d+(?:\.\d+){0,2}\z/.freeze
13
+
14
+ Record = Struct.new(
15
+ :project,
16
+ :target,
17
+ :configuration,
18
+ :original_effective,
19
+ :target_configuration_value,
20
+ :project_value,
21
+ :current,
22
+ :changed,
23
+ :configuration_object,
24
+ keyword_init: true
25
+ )
26
+
27
+ class AuditError < StandardError; end
28
+
29
+ module_function
30
+
31
+ # Raises low effective deployment settings to +minimum_ios_version+. A missing target setting
32
+ # resolves through its xcconfig and then the same-named project configuration. Numeric effective
33
+ # settings above the minimum are preserved. A selected non-numeric effective setting fails
34
+ # closed because its resolved value cannot be audited deterministically from the project.
35
+ def normalize!(installer, minimum_ios_version:, io: $stdout)
36
+ minimum = parse_version!(minimum_ios_version, context: "minimum iOS version")
37
+ targets = installer_targets(installer)
38
+ records = records_for(targets)
39
+ ensure_records!(records)
40
+ validate_numeric_settings!(records)
41
+
42
+ records = records.map do |record|
43
+ normalize_record!(record, minimum)
44
+ end
45
+
46
+ audit_records!(records, minimum)
47
+ save_integrated_user_projects!(installer, records)
48
+ write_changes(io, records)
49
+ write_summary(io, records, minimum)
50
+ records
51
+ end
52
+
53
+ # Audits an installer without modifying it. This is useful from a second post-install check.
54
+ def audit!(installer, minimum_ios_version:, io: $stdout)
55
+ minimum = parse_version!(minimum_ios_version, context: "minimum iOS version")
56
+ records = records_for(installer_targets(installer))
57
+ ensure_records!(records)
58
+
59
+ audit_records!(records, minimum)
60
+ write_summary(io, records, minimum)
61
+ records
62
+ end
63
+
64
+ # Audits projects loaded by Xcodeproj. The caller owns loading the projects so this helper does
65
+ # not add a runtime dependency on Xcodeproj to the normal Podfile integration path.
66
+ def audit_projects!(projects, minimum_ios_version:, io: $stdout)
67
+ minimum = parse_version!(minimum_ios_version, context: "minimum iOS version")
68
+ targets = projects.flat_map { |project| project.targets }
69
+ records = records_for(targets)
70
+ ensure_records!(records)
71
+
72
+ audit_records!(records, minimum)
73
+ write_records(io, records, minimum)
74
+ records
75
+ end
76
+
77
+ # Returns the greater of two numeric iOS deployment targets. React Native integrations use
78
+ # this to preserve React Native's own floor when it is higher than Zixflow's floor.
79
+ def maximum(first, second)
80
+ first_version = parse_version!(first, context: "iOS version")
81
+ second_version = parse_version!(second, context: "iOS version")
82
+ [first_version, second_version].max.to_s
83
+ end
84
+
85
+ def installer_targets(installer)
86
+ generated_projects = if installer.respond_to?(:generated_projects)
87
+ Array(installer.generated_projects).compact
88
+ else
89
+ []
90
+ end
91
+ if generated_projects.empty? && installer.respond_to?(:pods_project)
92
+ generated_projects = [installer.pods_project].compact
93
+ end
94
+
95
+ pod_targets = generated_projects.flat_map { |project| project.targets }
96
+ user_targets = if installer.respond_to?(:aggregate_targets)
97
+ Array(installer.aggregate_targets).flat_map do |aggregate_target|
98
+ aggregate_target.respond_to?(:user_targets) ? Array(aggregate_target.user_targets) : []
99
+ end
100
+ else
101
+ []
102
+ end
103
+
104
+ deduplicate_targets(pod_targets + user_targets)
105
+ end
106
+ private_class_method :installer_targets
107
+
108
+ def save_integrated_user_projects!(installer, records)
109
+ return unless installer.respond_to?(:aggregate_targets)
110
+
111
+ changed_project_paths = records.select(&:changed).map(&:project).uniq
112
+ return if changed_project_paths.empty?
113
+
114
+ projects = Array(installer.aggregate_targets).each_with_object([]) do |aggregate_target, result|
115
+ if aggregate_target.respond_to?(:user_project)
116
+ user_project = aggregate_target.user_project
117
+ result << user_project unless user_project.nil?
118
+ end
119
+ next unless aggregate_target.respond_to?(:user_targets)
120
+
121
+ Array(aggregate_target.user_targets).each do |target|
122
+ result << target.project if target.respond_to?(:project) && !target.project.nil?
123
+ end
124
+ end.compact
125
+
126
+ projects.select { |project| changed_project_paths.include?(project_path(project)) }
127
+ .uniq { |project| project_path(project) }
128
+ .each do |project|
129
+ unless project.respond_to?(:save)
130
+ raise AuditError, "Cannot save integrated user project #{project_path(project)}"
131
+ end
132
+
133
+ project.save
134
+ end
135
+ end
136
+ private_class_method :save_integrated_user_projects!
137
+
138
+ def deduplicate_targets(targets)
139
+ targets.each_with_object({}) do |target, unique|
140
+ project = target.respond_to?(:project) ? target.project : nil
141
+ project_key = project_path(project)
142
+ target_key = target.respond_to?(:uuid) ? target.uuid : target.object_id
143
+ unique[[project_key, target_key]] ||= target
144
+ end.values
145
+ end
146
+ private_class_method :deduplicate_targets
147
+
148
+ def normalize_record!(record, minimum)
149
+ current = record.current
150
+ current_version = current.nil? ? nil : parse_version!(current, context: record_context(record))
151
+ return record if current_version && current_version >= minimum
152
+
153
+ record.configuration_object.build_settings[BUILD_SETTING] = minimum.to_s
154
+ record.current = minimum.to_s
155
+ record.changed = true
156
+ record
157
+ end
158
+ private_class_method :normalize_record!
159
+
160
+ def records_for(targets)
161
+ targets.flat_map do |target|
162
+ project = target.respond_to?(:project) ? target.project : nil
163
+ target.build_configurations.map do |configuration|
164
+ context = configuration_context(project, target, configuration)
165
+ validate_no_conditional_settings!(configuration.build_settings, context: context)
166
+ target_setting_present = configuration.build_settings.key?(BUILD_SETTING)
167
+ target_value = normalized_setting(configuration.build_settings[BUILD_SETTING])
168
+ if target_setting_present
169
+ target_configuration_value = nil
170
+ project_value = nil
171
+ effective_value = target_value
172
+ else
173
+ target_configuration_present, target_configuration_value = configuration_file_setting(
174
+ configuration,
175
+ context: context
176
+ )
177
+ if target_configuration_present
178
+ project_value = nil
179
+ effective_value = target_configuration_value
180
+ else
181
+ project_configuration = matching_project_configuration(project, configuration.name)
182
+ project_value = effective_configuration_value(
183
+ project_configuration,
184
+ context: context
185
+ )
186
+ effective_value = project_value
187
+ end
188
+ end
189
+ Record.new(
190
+ project: project_path(project),
191
+ target: target.name.to_s,
192
+ configuration: configuration.name.to_s,
193
+ original_effective: effective_value,
194
+ target_configuration_value: target_configuration_value,
195
+ project_value: project_value,
196
+ current: effective_value,
197
+ changed: false,
198
+ configuration_object: configuration
199
+ )
200
+ end
201
+ end.sort_by { |record| [record.project, record.target, record.configuration] }
202
+ end
203
+ private_class_method :records_for
204
+
205
+ def effective_configuration_value(configuration, context:)
206
+ return nil if configuration.nil?
207
+
208
+ validate_no_conditional_settings!(configuration.build_settings, context: context)
209
+
210
+ if configuration.build_settings.key?(BUILD_SETTING)
211
+ return normalized_setting(configuration.build_settings[BUILD_SETTING])
212
+ end
213
+
214
+ _present, value = configuration_file_setting(configuration, context: context)
215
+ value
216
+ end
217
+ private_class_method :effective_configuration_value
218
+
219
+ def configuration_file_setting(configuration, context:)
220
+ synchronized_anchor = if configuration.respond_to?(:base_configuration_reference_anchor)
221
+ configuration.base_configuration_reference_anchor
222
+ end
223
+ synchronized_relative_path = if configuration.respond_to?(:base_configuration_reference_relative_path)
224
+ configuration.base_configuration_reference_relative_path
225
+ end
226
+ if synchronized_anchor || synchronized_relative_path
227
+ raise AuditError,
228
+ "#{context}: Cannot inspect synchronized-group xcconfig " \
229
+ "#{rendered_synchronized_path(synchronized_anchor, synchronized_relative_path)}. " \
230
+ "Use a standard xcconfig file reference before rerunning CocoaPods."
231
+ end
232
+
233
+ base_configuration_reference = if configuration.respond_to?(:base_configuration_reference)
234
+ configuration.base_configuration_reference
235
+ end
236
+ if base_configuration_reference
237
+ xcconfig_path = if base_configuration_reference.respond_to?(:real_path)
238
+ base_configuration_reference.real_path
239
+ end
240
+ unless xcconfig_path.respond_to?(:exist?) && xcconfig_path.exist? &&
241
+ xcconfig_path.respond_to?(:readable?) && xcconfig_path.readable?
242
+ rendered_path = xcconfig_path.nil? ? "(unresolved path)" : xcconfig_path.to_s
243
+ raise AuditError, "#{context}: Cannot read xcconfig #{rendered_path}"
244
+ end
245
+
246
+ unless defined?(::Xcodeproj::Config)
247
+ raise AuditError,
248
+ "#{context}: Cannot parse xcconfig #{xcconfig_path}; Xcodeproj::Config is unavailable"
249
+ end
250
+
251
+ begin
252
+ settings = ::Xcodeproj::Config.new(xcconfig_path).to_hash
253
+ rescue StandardError => error
254
+ raise AuditError,
255
+ "#{context}: Cannot parse xcconfig #{xcconfig_path}: " \
256
+ "#{error.class}: #{error.message}"
257
+ end
258
+
259
+ unless settings.respond_to?(:key?)
260
+ raise AuditError, "#{context}: Cannot inspect xcconfig #{xcconfig_path}"
261
+ end
262
+ validate_no_conditional_settings!(
263
+ settings,
264
+ context: "#{context}, xcconfig #{xcconfig_path}"
265
+ )
266
+ return [false, nil] unless settings.key?(BUILD_SETTING)
267
+
268
+ return [true, normalized_setting(settings[BUILD_SETTING])]
269
+ end
270
+
271
+ [false, nil]
272
+ end
273
+ private_class_method :configuration_file_setting
274
+
275
+ def validate_no_conditional_settings!(settings, context:)
276
+ conditional_setting_pattern = /\A#{Regexp.escape(BUILD_SETTING)}[[:space:]]*\[/
277
+ conditional_keys = settings.keys.map(&:to_s).select do |key|
278
+ key.match?(conditional_setting_pattern)
279
+ end.sort
280
+ return if conditional_keys.empty?
281
+
282
+ raise AuditError,
283
+ "#{context} has conditional #{BUILD_SETTING} settings that cannot be audited deterministically: " \
284
+ "#{conditional_keys.join(', ')}. Replace them with one numeric, unconditional #{BUILD_SETTING} " \
285
+ "before running this helper."
286
+ end
287
+ private_class_method :validate_no_conditional_settings!
288
+
289
+ def rendered_synchronized_path(anchor, relative_path)
290
+ anchor_path = if anchor.respond_to?(:path)
291
+ anchor.path
292
+ elsif anchor.respond_to?(:display_name)
293
+ anchor.display_name
294
+ else
295
+ "(unknown anchor)"
296
+ end
297
+ [anchor_path, relative_path].compact.map(&:to_s).reject(&:empty?).join("/")
298
+ end
299
+ private_class_method :rendered_synchronized_path
300
+
301
+ def matching_project_configuration(project, configuration_name)
302
+ return nil if project.nil? || !project.respond_to?(:build_configurations)
303
+
304
+ Array(project.build_configurations).find do |configuration|
305
+ configuration.name.to_s == configuration_name.to_s
306
+ end
307
+ end
308
+ private_class_method :matching_project_configuration
309
+
310
+ def ensure_records!(records)
311
+ return unless records.empty?
312
+
313
+ raise AuditError, "CocoaPods iOS deployment-target audit found no target configurations"
314
+ end
315
+ private_class_method :ensure_records!
316
+
317
+ def validate_numeric_settings!(records)
318
+ records.each do |record|
319
+ parse_version!(record.current, context: record_context(record)) unless record.current.nil?
320
+ end
321
+ end
322
+ private_class_method :validate_numeric_settings!
323
+
324
+ def audit_records!(records, minimum)
325
+ violations = records.each_with_object([]) do |record, result|
326
+ current = record.current
327
+ if current.nil?
328
+ result << "#{record_context(record)} is missing #{BUILD_SETTING}"
329
+ else
330
+ begin
331
+ version = parse_version!(current, context: record_context(record))
332
+ if version < minimum
333
+ result << "#{record_context(record)} is #{current}, below #{minimum}"
334
+ end
335
+ rescue AuditError => error
336
+ result << error.message
337
+ end
338
+ end
339
+ end
340
+
341
+ return if violations.empty?
342
+
343
+ raise AuditError, "CocoaPods iOS deployment-target audit failed:\n- #{violations.join("\n- ")}"
344
+ end
345
+ private_class_method :audit_records!
346
+
347
+ def write_summary(io, records, minimum)
348
+ return if io.nil?
349
+
350
+ changed = records.count(&:changed)
351
+ io.puts(
352
+ "Zixflow CocoaPods deployment-target audit passed: " \
353
+ "#{records.length} target configurations at iOS #{minimum} or newer (#{changed} normalized)."
354
+ )
355
+ end
356
+ private_class_method :write_summary
357
+
358
+ def write_changes(io, records)
359
+ return if io.nil?
360
+
361
+ records.select(&:changed).each do |record|
362
+ original = record.original_effective.nil? ? "missing" : record.original_effective
363
+ io.puts(
364
+ "Zixflow CocoaPods deployment target normalized: " \
365
+ "#{record_context(record)}: #{original} -> #{record.current}"
366
+ )
367
+ end
368
+ end
369
+ private_class_method :write_changes
370
+
371
+ def write_records(io, records, minimum)
372
+ return if io.nil?
373
+
374
+ io.puts(
375
+ "project\ttarget\tconfiguration\ttarget_#{BUILD_SETTING}\t" \
376
+ "target_xcconfig_#{BUILD_SETTING}\t" \
377
+ "project_#{BUILD_SETTING}\teffective_#{BUILD_SETTING}"
378
+ )
379
+ records.each do |record|
380
+ target_value = normalized_setting(record.configuration_object.build_settings[BUILD_SETTING])
381
+ io.puts(
382
+ [
383
+ record.project,
384
+ record.target,
385
+ record.configuration,
386
+ target_value,
387
+ record.target_configuration_value,
388
+ record.project_value,
389
+ record.current
390
+ ].join("\t")
391
+ )
392
+ end
393
+ write_summary(io, records, minimum)
394
+ end
395
+ private_class_method :write_records
396
+
397
+ def parse_version!(value, context:)
398
+ normalized = normalized_setting(value)
399
+ unless normalized&.match?(VERSION_PATTERN)
400
+ rendered = normalized.nil? ? "missing" : normalized.inspect
401
+ raise AuditError, "#{context} has non-numeric #{BUILD_SETTING}: #{rendered}"
402
+ end
403
+
404
+ Gem::Version.new(normalized)
405
+ end
406
+ private_class_method :parse_version!
407
+
408
+ def normalized_setting(value)
409
+ normalized = value.nil? ? nil : value.to_s.strip
410
+ normalized.nil? || normalized.empty? ? nil : normalized
411
+ end
412
+ private_class_method :normalized_setting
413
+
414
+ def record_context(record)
415
+ "#{record.project}: target #{record.target}, configuration #{record.configuration}"
416
+ end
417
+ private_class_method :record_context
418
+
419
+ def configuration_context(project, target, configuration)
420
+ "#{project_path(project)}: target #{target.name}, configuration #{configuration.name}"
421
+ end
422
+ private_class_method :configuration_context
423
+
424
+ def project_path(project)
425
+ return "unknown-project" if project.nil?
426
+ return project.path.to_s if project.respond_to?(:path) && project.path
427
+
428
+ project.to_s
429
+ end
430
+ private_class_method :project_path
431
+ end
432
+ end
@@ -1,7 +1,12 @@
1
1
  #import "utils/RCTZixflowUtils.h"
2
2
  #import <React/RCTBridgeModule.h>
3
+ #import <React/RCTInvalidating.h>
3
4
  #import <RNZixflowSpec/RNZixflowSpec.h>
4
5
 
6
+ @protocol NativeZixflowBridge <NativeZixflowSpec, RCTInvalidating>
7
+ - (void)setEventEmitter:(id)emitter;
8
+ @end
9
+
5
10
  // Objective-C wrapper for new architecture TurboModule implementation
6
11
  @interface RCTNativeZixflow : NSObject <NativeZixflowSpec>
7
12
  // Bridge to Swift implementation for cross-language compatibility
@@ -30,6 +35,7 @@ RCT_EXPORT_MODULE()
30
35
  RCT_ASSERT_NOT_NIL(swiftClass, @"NativeZixflow Swift class", @"during runtime lookup");
31
36
  _swiftBridge = [[swiftClass alloc] init];
32
37
  [self assertBridgeAvailable:@"creating NativeZixflow Swift instance"];
38
+ [_swiftBridge setEventEmitter:self];
33
39
  }
34
40
  return self;
35
41
  }
@@ -47,6 +53,37 @@ RCT_EXPORT_MODULE()
47
53
  [_swiftBridge initialize:config args:args resolve:resolve reject:reject];
48
54
  }
49
55
 
56
+ - (void)setDeepLinkRoutingReady {
57
+ [self assertBridgeAvailable:@"during setDeepLinkRoutingReady"];
58
+ [_swiftBridge setDeepLinkRoutingReady];
59
+ }
60
+
61
+ - (void)registerDeepLinkHandler {
62
+ [self assertBridgeAvailable:@"during registerDeepLinkHandler"];
63
+ [_swiftBridge registerDeepLinkHandler];
64
+ }
65
+
66
+ - (void)unregisterDeepLinkHandler {
67
+ [self assertBridgeAvailable:@"during unregisterDeepLinkHandler"];
68
+ [_swiftBridge unregisterDeepLinkHandler];
69
+ }
70
+
71
+ - (void)acknowledgeDeepLink:(NSString *)id handled:(BOOL)handled {
72
+ [self assertBridgeAvailable:@"during acknowledgeDeepLink"];
73
+ [_swiftBridge acknowledgeDeepLink:id handled:handled];
74
+ }
75
+
76
+ - (void)invalidate {
77
+ [self assertBridgeAvailable:@"during invalidate"];
78
+ [_swiftBridge invalidate];
79
+ }
80
+
81
+ // Pins the Codegen event name at compile time. Keep this in sync with the TypeScript spec and the
82
+ // runtime selector in NativeZixflow.swift.
83
+ - (void)emitOnDeepLinkReceived:(NSDictionary *)value {
84
+ [super emitOnDeepLinkReceived:value];
85
+ }
86
+
50
87
  - (void)identify:(NSDictionary *)params {
51
88
  [self assertBridgeAvailable:@"during identify"];
52
89
  [_swiftBridge identify:params];
@@ -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 {