zixflow-reactnative 1.1.3 → 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 (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 +40 -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 +17 -7
  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 +90 -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
@@ -0,0 +1,139 @@
1
+ # CocoaPods deployment-target normalization
2
+
3
+ Xcode validates the deployment target of every generated CocoaPods target, including dependency,
4
+ aggregate, privacy-manifest, and resource-bundle targets. Xcode 27 rejects targets below the build
5
+ range supported by its iOS SDK even when the dependency itself is source-compatible with your app.
6
+
7
+ The `zixflow-reactnative` package includes an opt-in Podfile helper. It raises low or missing
8
+ generated settings to the greater of React Native's current minimum and iOS 15.0. For React Native
9
+ 0.83 that value is iOS 15.1. It also covers integrated application, Notification Service
10
+ Extension, and widget targets, while preserving higher numeric deployment targets. When a target
11
+ setting is absent, the helper resolves its target xcconfig and then the same-named project build
12
+ configuration, preserving a higher inherited app or extension floor. The helper changes local build
13
+ settings in the generated Pods project and CocoaPods-integrated app or extension projects. It does
14
+ not rewrite a podspec or change runtime API availability.
15
+
16
+ The package owns and tests this copy of the helper because customer Podfiles must be able to load it
17
+ from the installed npm package. When its behavior changes, update the helper, tests, and guidance
18
+ together. It does not depend on a cross-repository source lock or require wrapper copies to remain
19
+ byte-identical.
20
+
21
+ Zixflow deliberately continues to publish native SDKs that support iOS versions below 15. A
22
+ podspec can therefore correctly declare that lower library minimum even when React Native or the
23
+ application consuming it requires a newer iOS version. CocoaPods carries deployment metadata from
24
+ Zixflow and third-party podspecs into generated build targets, but Xcode 27 no longer accepts
25
+ targets below the iOS SDK's supported build range. Raising every published podspec to iOS 15 would
26
+ unnecessarily drop older applications and would not control metadata from transitive
27
+ dependencies. The helper instead aligns the generated targets with the host application's chosen
28
+ minimum while leaving the packages' published runtime compatibility unchanged.
29
+ This is the supported integration policy for a React Native application that has moved its own
30
+ minimum to the required iOS floor.
31
+
32
+ > [!WARNING]
33
+ > This is an opt-in build migration. Integrated app and extension targets below the computed floor
34
+ > are raised to that floor. With React Native 0.83, shipping the resulting iOS 15.1 application
35
+ > means users on iOS 15.0 and earlier cannot install subsequent app updates. Adopt the helper only
36
+ > when your product has intentionally moved its application and extension deployment targets.
37
+
38
+ Resolve the helper through Node so hoisted and monorepo installations work, then call it after
39
+ `react_native_post_install` in the one existing `post_install` block:
40
+
41
+ ```ruby
42
+ def node_resolve(script)
43
+ Pod::Executable.execute_command('node', ['-p',
44
+ "require.resolve('#{script}', {paths: [process.argv[1]]})",
45
+ __dir__
46
+ ]).strip
47
+ end
48
+
49
+ customer_io_package_root = File.dirname(node_resolve('zixflow-reactnative/package.json'))
50
+ require File.join(customer_io_package_root, 'ios', 'cocoapods_deployment_target')
51
+
52
+ customer_io_minimum_ios_version = Zixflow::CocoaPodsDeploymentTarget.maximum(
53
+ min_ios_version_supported,
54
+ '15.0'
55
+ )
56
+
57
+ post_install do |installer|
58
+ react_native_post_install(
59
+ installer,
60
+ config[:reactNativePath],
61
+ :mac_catalyst_enabled => false
62
+ )
63
+
64
+ Zixflow::CocoaPodsDeploymentTarget.normalize!(
65
+ installer,
66
+ minimum_ios_version: customer_io_minimum_ios_version
67
+ )
68
+ end
69
+ ```
70
+
71
+ Remove the generated `ios/Pods` directory and run `pod install` again. When a target build-setting
72
+ key is absent, the helper resolves the target xcconfig and matching project configuration before
73
+ adding an override. A present target key is authoritative, so lower-precedence xcconfig and project
74
+ values are not inspected. This matches Xcode's treatment of an explicitly empty target value: it
75
+ is missing and must be normalized, rather than inherited from a lower-precedence value. For each
76
+ change, the helper prints a stable project, target, and configuration line with the original
77
+ effective value and final value. It fails the install if the selected effective value is
78
+ non-numeric, such as `$(CUSTOM_IOS_FLOOR)`, because the generated-project audit cannot prove its
79
+ resolved value. A non-numeric value at a lower precedence does not fail when an explicit target
80
+ setting already determines the effective value.
81
+ SDK-, architecture-, or configuration-qualified keys such as
82
+ `IPHONEOS_DEPLOYMENT_TARGET[sdk=iphoneos*]` also fail before mutation at the selected precedence.
83
+ Xcode can choose a qualified key over an unconditional value, so adding a numeric override would
84
+ not prove the effective floor and could lower a higher conditional project setting. Replace the
85
+ selected conditional matrix with one numeric, unconditional deployment target before rerunning the
86
+ helper.
87
+
88
+ If the helper reports a non-numeric selected value, use the project, target, and configuration in
89
+ the error to locate the authoritative `IPHONEOS_DEPLOYMENT_TARGET`. Replace the macro or inherited
90
+ expression at that precedence with its intended numeric version, then run `pod install` again. For
91
+ a generated Pods target, make that override durably in the Podfile's `post_install` hook before
92
+ calling `Zixflow::CocoaPodsDeploymentTarget.normalize!`, or correct the podspec that supplies the
93
+ expression; do not edit the generated project because the next install replaces it. Do not skip
94
+ that target: continuing with an unresolved expression would leave the generated project outside
95
+ the deterministic audit. The helper validates every selected value before changing any project,
96
+ so this failure does not leave a partially normalized installation.
97
+
98
+ If an error says a selected xcconfig cannot be read or parsed, repair or remove the reported base
99
+ configuration file reference for the reported project, target, and configuration. Lower-precedence
100
+ xcconfigs are not parsed when the target build-setting key is present. Run the helper in the
101
+ CocoaPods Ruby environment so the public `Xcodeproj::Config` parser is available.
102
+ Synchronized-group xcconfig references are not resolved by that public parser; if the helper
103
+ reports one, replace it with a standard xcconfig file reference, then run `pod install` again.
104
+ These cases fail before any project mutation.
105
+
106
+ ## When the helper is no longer needed
107
+
108
+ Keep the helper while a supported dependency graph can validly include deployment metadata below
109
+ the host application's minimum. This is expected while Zixflow supports older iOS versions or
110
+ supported third-party pods continue to declare lower minimums. A `platform` declaration in the
111
+ application's Podfile alone does not guarantee that every generated target uses the same value.
112
+
113
+ The helper becomes unnecessary only when a clean install without it proves that every
114
+ target/configuration in every supported React Native and push-provider graph declares an effective
115
+ numeric deployment target at or above the host application's minimum. That state would normally
116
+ follow an intentional platform-support change across the SDK, wrappers, and relevant dependencies;
117
+ it is not a prerequisite for adopting Xcode 27. Keep the audit in CI after removing the helper so
118
+ a later dependency update cannot silently reintroduce a lower target.
119
+
120
+ Repository contributors also have a deterministic CI audit under `scripts/` that prints the target,
121
+ matching project, and effective value for every generated target/configuration pair in stable
122
+ order. That audit script is not part of the published npm package. It examines every target in each
123
+ passed project, including non-integrated targets that the normalizer intentionally does not change;
124
+ set those targets to the host minimum explicitly. Pass the `Pods` directory so the audit discovers
125
+ every `.xcodeproj` directly under it, including CocoaPods multi-project output, while ignoring
126
+ unrelated example projects vendored inside downloaded pod sources. It fails if a supplied path is
127
+ missing or contains no projects.
128
+
129
+ ```sh
130
+ cd example
131
+ bundle exec ruby ../scripts/audit_cocoapods_deployment_targets.rb \
132
+ --minimum 15.1 \
133
+ ios/Pods \
134
+ ios/SampleApp.xcodeproj
135
+ ```
136
+
137
+ Keep this audit next to the React Native simulator build and unsigned generic-device archive.
138
+ Passing those checks does not prove real-device push delivery, signed archive export, or App Store
139
+ submission.
@@ -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];