react-native-acoustic-connect-beta 19.1.5 → 19.1.7

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.
@@ -52,7 +52,15 @@ iOSVersion = connectConfig["Connect"]["iOSVersion"]
52
52
  # `ConnectDelegateProxy`-swizzled `UNUserNotificationCenter` delegate path
53
53
  # the bridge relies on in `pushMode: 'automatic'`). 2.1.12 and older mis-wire
54
54
  # that path, so the floor is raised to guarantee the fix is present.
55
- sdkFloor = '>= 2.1.13'
55
+ # 2.1.51 floor: the first AcousticConnectDebug build with every iOS fix this
56
+ # release relies on. The bridge now captures the react-native-screens screen on
57
+ # display, and the SDK stops sending a duplicate, self-referencing screen view
58
+ # (from 2.1.44); an on-screen Alert.alert no longer re-posts the current screen
59
+ # while screenshots are on (from 2.1.49); and React Native's dev loading banner
60
+ # is no longer logged as a screen (2.1.51). Earlier builds still resolve and
61
+ # compile, but keep those defects. Open-ended on purpose: 2.2.x and newer
62
+ # satisfy it too.
63
+ sdkFloor = '>= 2.1.51'
56
64
  dependencyRequirements = iOSVersion.to_s.empty? ? [sdkFloor] : [sdkFloor, iOSVersion]
57
65
 
58
66
  # Normalize Connect.PushEnabled to a STRICT boolean before writing the native
@@ -148,6 +156,7 @@ Pod::Spec.new do |s|
148
156
  ts.source_files = [
149
157
  'ios/Tests/**/*.swift',
150
158
  'ios/ConnectRNParsing.swift',
159
+ 'ios/ConnectRNScreenResolution.swift',
151
160
  'nitrogen/generated/ios/swift/Variant_Bool_String_Double.swift',
152
161
  ]
153
162
  # ios/ConnectRNParsing.swift does `import Connect`. CocoaPods test_specs
package/CHANGELOG.md CHANGED
@@ -1,3 +1,13 @@
1
+ ## 19.1.7 (2026-10-06)
2
+
3
+ ### Reverts
4
+
5
+ * Revert "Beta ReactNativeConnect build: 18.0.36" ([609ad7d](https://github.com/aipoweredmarketer/react-native-acoustic-connect-beta/commit/609ad7d3244ec06f27dced5864d40585c5b3c9cf))
6
+ ## 19.1.6 (2026-10-01)
7
+
8
+ ### Reverts
9
+
10
+ * Revert "Beta ReactNativeConnect build: 18.0.36" ([609ad7d](https://github.com/aipoweredmarketer/react-native-acoustic-connect-beta/commit/609ad7d3244ec06f27dced5864d40585c5b3c9cf))
1
11
  ## 19.1.5 (2026-10-01)
2
12
 
3
13
  ### Reverts
package/README.md CHANGED
@@ -19,8 +19,8 @@ These requirements apply to SDK **19.1** and later. If your app is on React Nati
19
19
  - Node 20.19.4 or newer for RN 0.84–0.86. **RN 0.87 requires Node 22.13 or newer** (`^22.13.0 || ^24.3.0 || >= 26`) and will not install on Node 20 at all
20
20
  - JDK 17 or newer for Android builds — the same JDK React Native itself requires.
21
21
  The Android module compiles at Java 17; newer JDKs work.
22
- - iOS deployment target ≥ 15.1, AcousticConnect / AcousticConnectDebug pod ≥ 2.0.5
23
- - Android `minSdk` ≥ 26, `compileSdk` ≥ 35, `io.github.go-acoustic:connect` in `[11.0.11, 12.0.0)`
22
+ - iOS deployment target ≥ 15.1, AcousticConnect / AcousticConnectDebug pod ≥ 2.1.51
23
+ - Android `minSdk` ≥ 26, `compileSdk` ≥ 35, `io.github.go-acoustic:connect` in `[11.1.16-beta, 12.0.0)` (Connect Android 11.1 or newer)
24
24
  - **Expo SDK 56+ is supported** from 19.1 (Expo SDK 55 → use 19.0.x) via the bundled Expo Config Plugin —
25
25
  development builds only (Expo Go is not supported); see
26
26
  [Using with Expo SDK 56+](#using-with-expo-sdk-56).
@@ -537,14 +537,13 @@ AcousticConnectRN.logSignal(pageView, 1)
537
537
  > at the top level but dropped a top-level number. Numbers nested inside an
538
538
  > object or array were never affected on either platform.
539
539
  >
540
- > Both versions sit inside the `[11.0.11, 12.0.0)` range this package accepts,
541
- > and the Android dependency floats to the newest published build unless you pin
542
- > it, so most integrations get the new behaviour. Nest the number if you pin an
543
- > older one:
540
+ > The Android versions in that note are below the `[11.1.16-beta, 12.0.0)` range this
541
+ > package accepts, so every supported Android version carries a top-level number.
542
+ > Nesting the number stays portable across both platforms:
544
543
  >
545
544
  > ```ts
546
545
  > AcousticConnectRN.logSignal({ cart: { items: 3 } }, 1) // any supported version
547
- > AcousticConnectRN.logSignal({ items: 3 }, 1) // iOS, + Android 11.0.24-beta and newer
546
+ > AcousticConnectRN.logSignal({ items: 3 }, 1) // any supported version
548
547
  > ```
549
548
 
550
549
  ### `AcousticConnectRN.logCustomEvent(eventName, values, level): boolean`
@@ -996,15 +995,15 @@ that **changing `android.package` requires a clean prebuild**
996
995
  (`npx expo prebuild --platform android --clean`); an incremental prebuild keeps
997
996
  the stale `applicationId`.
998
997
 
999
- ### iOS — `pod install` fails with `[Connect] requires AcousticConnect >= 2.0.5`
998
+ ### iOS — `pod install` fails with `[Connect] requires AcousticConnect >= 2.1.51`
1000
999
 
1001
1000
  You've pinned an older `iOSVersion` in `ConnectConfig.json`. Bump it to a
1002
- 2.0.5+ release (or leave it empty for the newest available) and re-run
1001
+ 2.1.51 or newer (or leave it empty for the newest available) and re-run
1003
1002
  `pod install`.
1004
1003
 
1005
1004
  ### Android — Gradle resolution fails on `io.github.go-acoustic:connect`
1006
1005
 
1007
- The strict constraint at `[11.0.11, 12.0.0)` is rejecting your pin. Bump
1006
+ The strict constraint at `[11.1.16-beta, 12.0.0)` is rejecting your pin. Bump
1008
1007
  `AndroidVersion` in `ConnectConfig.json` to a release within that range (or
1009
1008
  leave it empty for the newest 11.x available). 12.x is intentionally outside
1010
1009
  the supported range pending compatibility validation — track that work
@@ -189,9 +189,19 @@ println "AcousticConnectRN: PushEnabled=$acousticPushEnabled, useRelease=$acoust
189
189
  // constraints below. Checked up front because the constraint cannot reject a
190
190
  // pin on its own: a strict range is itself a request for the newest version in
191
191
  // range, so an out-of-range pin was silently replaced by that version.
192
- def acousticMinVersion = [11, 0, 11]
192
+ // The lower bound is the first published Connect Android build with everything
193
+ // the bridge relies on: the Connect APIs it calls (`canTakeScreenShot`,
194
+ // `getLayoutInfo`, `logDialogLayout`, from 11.1.1-beta), the screen-view fix
195
+ // that logs a return to a screen after one that is not captured (11.1.13-beta),
196
+ // and password fields captured as dots rather than as typed (11.1.16-beta).
197
+ // Gradle orders `11.1.16-beta` below `11.1.16` (a `-beta` qualifier sorts before
198
+ // the release with the same numbers), so the range admits the 11.1.16 release
199
+ // and everything newer. The numeric pin check below ignores qualifiers, so it
200
+ // compares against the bare [11, 1, 16].
201
+ def acousticMinVersion = [11, 1, 16]
202
+ def acousticMinVersionQualifier = '-beta'
193
203
  def acousticMaxVersionExclusive = [12, 0, 0]
194
- def acousticSupportedRange = "[${acousticMinVersion.join('.')}, ${acousticMaxVersionExclusive.join('.')})"
204
+ def acousticSupportedRange = "[${acousticMinVersion.join('.')}${acousticMinVersionQualifier}, ${acousticMaxVersionExclusive.join('.')})"
195
205
  if (!acousticVersionPin.isEmpty()) {
196
206
  def m = acousticVersionPin =~ /^(\d+)\.(\d+)\.(\d+)/
197
207
  def compare = { a, b -> [a, b].transpose().collect { x, y -> x <=> y }.find { it != 0 } ?: 0 }
@@ -340,7 +350,7 @@ dependencies {
340
350
  strictly acousticSupportedRange
341
351
  if (!acousticVersionPin.isEmpty()) { prefer acousticVersionPin }
342
352
  }
343
- because 'AcousticConnectRN requires Connect Android SDK in [11.0.11, 12.0.0). Bump the upper bound after validating compatibility with a new major.'
353
+ because "AcousticConnectRN requires Connect Android SDK in ${acousticSupportedRange} (the bridge uses Connect APIs added in 11.1). Bump the upper bound after validating compatibility with a new major."
344
354
  }
345
355
  implementation('io.github.go-acoustic:connect-push-fcm') {
346
356
  version {
@@ -1,4 +1,4 @@
1
- #Thu Oct 01 14:25:53 GMT 2026
1
+ #Tue Oct 06 08:39:10 GMT 2026
2
2
  UseWhiteList=true
3
3
  PrintScreen=3
4
4
  UseRandomSample=false
@@ -1547,10 +1547,9 @@ class HybridAcousticConnectRN : HybridAcousticConnectRNSpec(),
1547
1547
  * error, and nothing here could rescue it. Numbers nested inside an object
1548
1548
  * or array were never affected — the enclosing [JSONObject]/[JSONArray] is
1549
1549
  * built here, so `org.json` serializes them normally — and iOS has always
1550
- * carried top-level numbers. Both sides of the boundary sit inside the
1551
- * `[11.0.11, 12.0.0)` range this bridge accepts, so an integration pinning
1552
- * an older Connect version still sees the old behaviour; nesting a number
1553
- * is portable across the whole range.
1550
+ * carried top-level numbers. Both sides of the boundary sit below the
1551
+ * `[11.1.16-beta, 12.0.0)` range this bridge accepts, so every supported
1552
+ * version carries a top-level number; nesting a number stays portable.
1554
1553
  *
1555
1554
  * A [JSONException] aborts the whole payload rather than escaping to the
1556
1555
  * caller. Non-finite numbers are the case that raises it: `org.json`
@@ -7,7 +7,8 @@
7
7
  # app target and the push extensions (ConnectNSE / ConnectNCE) all link the SAME
8
8
  # SDK build:
9
9
  # - useRelease => 'AcousticConnect', otherwise 'AcousticConnectDebug'
10
- # - floor '>= 2.1.13' (push permission API + automatic-mode fix floor)
10
+ # - floor '>= 2.1.51' (screen-capture, alert and dev-banner fixes; includes the push permission API
11
+ # and automatic-mode fix)
11
12
  # - optional exact pin via Connect.iOSVersion
12
13
  #
13
14
  # Usage from a Podfile (resolve the file the same way the RN template resolves
@@ -42,7 +43,7 @@ def acoustic_connect_pod(podfile_dir, config_path: nil)
42
43
  use_release = connect_config['useRelease']
43
44
  ios_version = connect_config['iOSVersion'].to_s
44
45
  name = use_release ? 'AcousticConnect' : 'AcousticConnectDebug'
45
- floor = '>= 2.1.13'
46
+ floor = '>= 2.1.51'
46
47
  requirements = ios_version.empty? ? [floor] : [floor, ios_version]
47
48
  [name, requirements]
48
49
  end
@@ -0,0 +1,116 @@
1
+ //
2
+ // Copyright (C) 2026 Acoustic, L.P. All rights reserved.
3
+ //
4
+ // NOTICE: This file contains material that is confidential and proprietary to
5
+ // Acoustic, L.P. and/or other developers. No license is granted under any
6
+ // intellectual or industrial property rights of Acoustic, L.P. except as may
7
+ // be provided in an agreement with Acoustic, L.P. Any unauthorized copying or
8
+ // distribution of content from this file is prohibited.
9
+ //
10
+
11
+ import UIKit
12
+
13
+ /// Walks from a window's root view controller down to the screen a layout
14
+ /// capture should describe.
15
+ ///
16
+ /// Split out of `HybridAcousticConnectRN` so the walk can be unit-tested
17
+ /// without a window, and compiled into the UnitTests bundle (see the
18
+ /// podspec's test_spec for why the pod module itself cannot be imported).
19
+ ///
20
+ /// Must be called on the main thread.
21
+ enum ConnectRNScreenResolution {
22
+
23
+ /// The view controller that is actually showing under `root`.
24
+ ///
25
+ /// Walks the `presentedViewController` chain, unwraps navigation and tab
26
+ /// controllers to the child on screen, and descends into
27
+ /// react-native-screens' controllers (see `activeScreen(in:)`).
28
+ ///
29
+ /// The react-native-screens step is what makes a capture name the same
30
+ /// controller the SDK records for the screen. Those controllers are plain
31
+ /// child view controllers of React Native's root, not navigation or tab
32
+ /// containers, so without it the walk stopped at the root. The SDK, which
33
+ /// records the `RNSScreen` from its `viewDidAppear`, then saw a capture of
34
+ /// some other controller and posted a second LOAD for a screen already on
35
+ /// display, naming itself as its referrer.
36
+ ///
37
+ /// - Parameter root: The window's root view controller.
38
+ /// - Returns: The view controller to capture, or `nil` when `root` is nil.
39
+ static func topViewController(from root: UIViewController?) -> UIViewController? {
40
+ var top = root
41
+ // Bounded rather than an open `while`. In practice the chain is a short
42
+ // linked list, but this runs on every navigation, so a pathological
43
+ // cycle must not hang the main thread.
44
+ var hops = 0
45
+ while hops < 32, let current = top {
46
+ hops += 1
47
+ // Presentation first: `presentedViewController` also reports a
48
+ // modal put up by a descendant, so checking it before unwrapping a
49
+ // container lands on the modal directly instead of on the
50
+ // container's child underneath it.
51
+ if let presented = current.presentedViewController {
52
+ top = presented
53
+ continue
54
+ }
55
+ if let nav = current as? UINavigationController,
56
+ let visible = nav.topViewController {
57
+ top = visible
58
+ continue
59
+ }
60
+ if let tabs = current as? UITabBarController,
61
+ let selected = tabs.selectedViewController {
62
+ top = selected
63
+ continue
64
+ }
65
+ if let screen = activeScreen(in: current) {
66
+ top = screen
67
+ continue
68
+ }
69
+ break
70
+ }
71
+ return top
72
+ }
73
+
74
+ /// The react-native-screens child of `viewController` that is showing, if
75
+ /// it has one.
76
+ ///
77
+ /// - A screen container (`RNSViewController`) is asked through its own
78
+ /// `findActiveChildVC`, which react-native-screens also uses to route
79
+ /// status-bar and orientation queries to the screen on top; during a
80
+ /// transition several screens are attached and only it knows which one.
81
+ /// - Anything else descends into its last visible react-native-screens
82
+ /// child: React Native's root holds the container that way, and a
83
+ /// screen holds a nested native stack (`RNSNavigationController`) that way.
84
+ ///
85
+ /// Only react-native-screens controllers are descended into, recognised by
86
+ /// the class-name prefix the SDK itself matches on (`isRNSScreen:`), so an
87
+ /// app that embeds React Native next to its own child controllers keeps
88
+ /// the controller it had before.
89
+ ///
90
+ /// - Parameter viewController: The controller the walk has reached.
91
+ /// - Returns: The child to continue from, or `nil` to stop at `viewController`.
92
+ static func activeScreen(in viewController: UIViewController) -> UIViewController? {
93
+ if isScreensController(viewController), let active = findActiveChild(of: viewController) {
94
+ return active
95
+ }
96
+ return viewController.children.last { child in
97
+ isScreensController(child) && child.viewIfLoaded?.isHidden != true
98
+ }
99
+ }
100
+
101
+ /// Whether `viewController` belongs to react-native-screens.
102
+ static func isScreensController(_ viewController: UIViewController) -> Bool {
103
+ NSStringFromClass(type(of: viewController)).hasPrefix("RNS")
104
+ }
105
+
106
+ private static let findActiveChildSelector = NSSelectorFromString("findActiveChildVC")
107
+
108
+ private static func findActiveChild(of container: UIViewController) -> UIViewController? {
109
+ guard container.responds(to: findActiveChildSelector),
110
+ let active = container.perform(findActiveChildSelector)?.takeUnretainedValue() as? UIViewController,
111
+ active.parent === container else {
112
+ return nil
113
+ }
114
+ return active
115
+ }
116
+ }
@@ -790,6 +790,11 @@ class HybridAcousticConnectRN: HybridAcousticConnectRNSpec {
790
790
  /// The framework only unwraps navigation controllers; capturing a tab
791
791
  /// controller is not wrong (its view contains the selected child), but
792
792
  /// the selected child is the screen the capture is named after.
793
+ /// - It descends into react-native-screens' controllers to the screen on
794
+ /// top (`ConnectRNScreenResolution`). The framework records that
795
+ /// `RNSScreen` as the current screen, and only a capture of the same
796
+ /// controller adds a layout to its screen view instead of posting a
797
+ /// second, self-referencing LOAD.
793
798
  ///
794
799
  /// Must be called on the main thread — see `captureScreenLayout`.
795
800
  private static func resolveTopViewController() -> UIViewController? {
@@ -810,34 +815,7 @@ class HybridAcousticConnectRN: HybridAcousticConnectRNSpec {
810
815
  return nil
811
816
  }
812
817
 
813
- var top = window.rootViewController
814
- // Bounded rather than an open `while`. In practice the chain is a short
815
- // linked list, but this runs on every navigation, so a pathological
816
- // cycle must not hang the main thread.
817
- var hops = 0
818
- while hops < 32 {
819
- hops += 1
820
- // Presentation first: `presentedViewController` also reports a
821
- // modal put up by a descendant, so checking it before unwrapping a
822
- // container lands on the modal directly instead of on the
823
- // container's child underneath it.
824
- if let presented = top?.presentedViewController {
825
- top = presented
826
- continue
827
- }
828
- if let nav = top as? UINavigationController,
829
- let visible = nav.topViewController {
830
- top = visible
831
- continue
832
- }
833
- if let tabs = top as? UITabBarController,
834
- let selected = tabs.selectedViewController {
835
- top = selected
836
- continue
837
- }
838
- break
839
- }
840
- return top
818
+ return ConnectRNScreenResolution.topViewController(from: window.rootViewController)
841
819
  }
842
820
 
843
821
  /// Resolves the top view controller and hands it to the framework, as one
@@ -187,9 +187,9 @@ export interface AcousticConnectRN extends HybridObject<{
187
187
  * map and dropped a top-level number. Numbers nested *inside* an object
188
188
  * or array were never affected, because the bridge builds those
189
189
  * `JSONObject`/`JSONArray` values itself, and iOS has always carried
190
- * top-level numbers normally. Both versions sit inside the
191
- * `[11.0.11, 12.0.0)` range this package accepts, so nest the number if
192
- * your integration pins a Connect Android version below 11.0.24-beta.
190
+ * top-level numbers normally. Both versions sit below the
191
+ * `[11.1.16-beta, 12.0.0)` range this package accepts, so every supported
192
+ * version carries a top-level number.
193
193
  *
194
194
  * @example Nested payload
195
195
  * ```ts
package/package.json CHANGED
@@ -228,7 +228,7 @@
228
228
  "source": "src/index",
229
229
  "summary": "react-native ios android tealeaf connect cxa wxca er enhanced-replay",
230
230
  "types": "./lib/typescript/src/index.d.ts",
231
- "version": "19.1.5",
231
+ "version": "19.1.7",
232
232
  "workspaces": [
233
233
  "example",
234
234
  "Examples/bare-workflow"
@@ -53,7 +53,7 @@ export declare function buildNSEEntitlements(appGroupIdentifier: string): string
53
53
  *
54
54
  * Tolerates a missing ConnectConfig.json or a missing `Connect` key: when
55
55
  * the file or key is absent the block defaults to AcousticConnectDebug with
56
- * floor '>= 2.1.12' and no version pin, so prop-only setups (no
56
+ * floor '>= 2.1.51' and no version pin, so prop-only setups (no
57
57
  * ConnectConfig.json) keep working. If the file exists but contains invalid
58
58
  * JSON, `pod install` will print a warning and use the same defaults rather
59
59
  * than crashing.
@@ -179,7 +179,7 @@ const PODFILE_MARKER = '# @generated ConnectNSE target (react-native-acoustic-co
179
179
  *
180
180
  * Tolerates a missing ConnectConfig.json or a missing `Connect` key: when
181
181
  * the file or key is absent the block defaults to AcousticConnectDebug with
182
- * floor '>= 2.1.12' and no version pin, so prop-only setups (no
182
+ * floor '>= 2.1.51' and no version pin, so prop-only setups (no
183
183
  * ConnectConfig.json) keep working. If the file exists but contains invalid
184
184
  * JSON, `pod install` will print a warning and use the same defaults rather
185
185
  * than crashing.
@@ -201,7 +201,7 @@ def ${helperName}
201
201
  use_release = connect_config['useRelease'] || false
202
202
  ios_version = (connect_config['iOSVersion'] || '').to_s
203
203
  name = use_release ? 'AcousticConnect' : 'AcousticConnectDebug'
204
- floor = '>= 2.1.12'
204
+ floor = '>= 2.1.51'
205
205
  requirements = ios_version.empty? ? [floor] : [floor, ios_version]
206
206
  [name, requirements]
207
207
  end
@@ -210,7 +210,7 @@ const PODFILE_MARKER =
210
210
  *
211
211
  * Tolerates a missing ConnectConfig.json or a missing `Connect` key: when
212
212
  * the file or key is absent the block defaults to AcousticConnectDebug with
213
- * floor '>= 2.1.12' and no version pin, so prop-only setups (no
213
+ * floor '>= 2.1.51' and no version pin, so prop-only setups (no
214
214
  * ConnectConfig.json) keep working. If the file exists but contains invalid
215
215
  * JSON, `pod install` will print a warning and use the same defaults rather
216
216
  * than crashing.
@@ -236,7 +236,7 @@ def ${helperName}
236
236
  use_release = connect_config['useRelease'] || false
237
237
  ios_version = (connect_config['iOSVersion'] || '').to_s
238
238
  name = use_release ? 'AcousticConnect' : 'AcousticConnectDebug'
239
- floor = '>= 2.1.12'
239
+ floor = '>= 2.1.51'
240
240
  requirements = ios_version.empty? ? [floor] : [floor, ios_version]
241
241
  [name, requirements]
242
242
  end
@@ -205,9 +205,9 @@ export interface AcousticConnectRN extends HybridObject<{ ios: 'swift', android:
205
205
  * map and dropped a top-level number. Numbers nested *inside* an object
206
206
  * or array were never affected, because the bridge builds those
207
207
  * `JSONObject`/`JSONArray` values itself, and iOS has always carried
208
- * top-level numbers normally. Both versions sit inside the
209
- * `[11.0.11, 12.0.0)` range this package accepts, so nest the number if
210
- * your integration pins a Connect Android version below 11.0.24-beta.
208
+ * top-level numbers normally. Both versions sit below the
209
+ * `[11.1.16-beta, 12.0.0)` range this package accepts, so every supported
210
+ * version carries a top-level number.
211
211
  *
212
212
  * @example Nested payload
213
213
  * ```ts