react-native-acoustic-connect-beta 19.1.4 → 19.1.6
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.
- package/AcousticConnectRN.podspec +1 -0
- package/CHANGELOG.md +10 -0
- package/README.md +1 -1
- package/android/build.gradle +97 -46
- package/android/config.gradle +92 -27
- package/android/src/main/assets/ConnectBasicConfig.properties +1 -1
- package/ios/ConnectRNScreenResolution.swift +116 -0
- package/ios/HybridAcousticConnectRN.swift +6 -28
- package/package.json +1 -1
|
@@ -148,6 +148,7 @@ Pod::Spec.new do |s|
|
|
|
148
148
|
ts.source_files = [
|
|
149
149
|
'ios/Tests/**/*.swift',
|
|
150
150
|
'ios/ConnectRNParsing.swift',
|
|
151
|
+
'ios/ConnectRNScreenResolution.swift',
|
|
151
152
|
'nitrogen/generated/ios/swift/Variant_Bool_String_Double.swift',
|
|
152
153
|
]
|
|
153
154
|
# ios/ConnectRNParsing.swift does `import Connect`. CocoaPods test_specs
|
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
## 19.1.6 (2026-10-01)
|
|
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.5 (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.4 (2026-09-30)
|
|
2
12
|
|
|
3
13
|
### Reverts
|
package/README.md
CHANGED
|
@@ -106,7 +106,7 @@ Field summary (see [API reference](#api-reference) for full semantics):
|
|
|
106
106
|
| `AppKey` | _(required)_ | Your Connect application key. |
|
|
107
107
|
| `PostMessageUrl` | _(required)_ | Collector endpoint URL. |
|
|
108
108
|
| `KillSwitchUrl` | _(optional)_ | Kill-switch endpoint URL. |
|
|
109
|
-
| `useRelease` | `false` | `true` selects the release AcousticConnect
|
|
109
|
+
| `useRelease` | `false` | Release vs. beta native SDKs. iOS: `true` selects the release AcousticConnect pod over the debug variant. Android: `false` resolves Connect from the GitHub-hosted beta Maven repository (betas are not published to Maven Central); `true` resolves from Maven Central only and rejects beta versions. If your `settings.gradle` sets `repositoriesMode` to `PREFER_SETTINGS`, project repositories are ignored, so add the beta repository there yourself — the build prints the exact entry. |
|
|
110
110
|
| `iOSVersion` | `""` | Pin a specific iOS pod version; empty = newest in the supported range. |
|
|
111
111
|
| `AndroidVersion` | `""` | Pin a specific Android Connect SDK version; empty = newest in the supported range. |
|
|
112
112
|
| `PushEnabled` | `false` | Master switch. On Android, also gates the `connect-push-fcm` artifact inclusion. |
|
package/android/build.gradle
CHANGED
|
@@ -165,6 +165,46 @@ android {
|
|
|
165
165
|
}
|
|
166
166
|
}
|
|
167
167
|
|
|
168
|
+
// Read consumer config at script-evaluation time so the right Connect Android
|
|
169
|
+
// artifact is selected without depending on config.gradle's text-rewriting
|
|
170
|
+
// pass (which only matches the analytics artifact name). Read first, because
|
|
171
|
+
// the repositories below depend on it:
|
|
172
|
+
// - `PushEnabled` picks between the analytics-only and push-fcm artifacts.
|
|
173
|
+
// - `AndroidVersion` (when set) pins the version against the active artifact.
|
|
174
|
+
// - `useRelease` picks where Connect comes from (see `allprojects` below).
|
|
175
|
+
import groovy.json.JsonSlurper
|
|
176
|
+
def acousticConfigFile = new File("$rootDir/../ConnectConfig.json")
|
|
177
|
+
def acousticConfig = acousticConfigFile.exists()
|
|
178
|
+
? new JsonSlurper().parse(acousticConfigFile)
|
|
179
|
+
: [Connect: [:]]
|
|
180
|
+
def acousticPushEnabled = (acousticConfig.Connect?.PushEnabled ?: false).toString().toBoolean()
|
|
181
|
+
def acousticUseRelease = (acousticConfig.Connect?.useRelease ?: false).toString().toBoolean()
|
|
182
|
+
def acousticVersionPin = (acousticConfig.Connect?.AndroidVersion ?: '').trim()
|
|
183
|
+
def acousticVersion = acousticVersionPin.isEmpty() ? '+' : acousticVersionPin
|
|
184
|
+
def acousticArtifact = acousticPushEnabled ? 'connect-push-fcm' : 'connect'
|
|
185
|
+
|
|
186
|
+
println "AcousticConnectRN: PushEnabled=$acousticPushEnabled, useRelease=$acousticUseRelease, artifact=io.github.go-acoustic:$acousticArtifact:$acousticVersion"
|
|
187
|
+
|
|
188
|
+
// The supported Connect Android range — the same bounds as the `strictly`
|
|
189
|
+
// constraints below. Checked up front because the constraint cannot reject a
|
|
190
|
+
// pin on its own: a strict range is itself a request for the newest version in
|
|
191
|
+
// range, so an out-of-range pin was silently replaced by that version.
|
|
192
|
+
def acousticMinVersion = [11, 0, 11]
|
|
193
|
+
def acousticMaxVersionExclusive = [12, 0, 0]
|
|
194
|
+
def acousticSupportedRange = "[${acousticMinVersion.join('.')}, ${acousticMaxVersionExclusive.join('.')})"
|
|
195
|
+
if (!acousticVersionPin.isEmpty()) {
|
|
196
|
+
def m = acousticVersionPin =~ /^(\d+)\.(\d+)\.(\d+)/
|
|
197
|
+
def compare = { a, b -> [a, b].transpose().collect { x, y -> x <=> y }.find { it != 0 } ?: 0 }
|
|
198
|
+
def parts = m.find() ? [m.group(1), m.group(2), m.group(3)].collect { it as int } : null
|
|
199
|
+
if (parts == null || compare(parts, acousticMinVersion) < 0 || compare(parts, acousticMaxVersionExclusive) >= 0) {
|
|
200
|
+
throw new GradleException(
|
|
201
|
+
"AcousticConnectRN: ConnectConfig.json Connect.AndroidVersion '$acousticVersionPin' is outside the " +
|
|
202
|
+
"supported Connect Android range $acousticSupportedRange. " +
|
|
203
|
+
"Set a version in that range, or leave it empty to use the newest."
|
|
204
|
+
)
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
168
208
|
repositories {
|
|
169
209
|
mavenCentral()
|
|
170
210
|
google()
|
|
@@ -174,36 +214,33 @@ allprojects {
|
|
|
174
214
|
repositories {
|
|
175
215
|
google()
|
|
176
216
|
|
|
177
|
-
//
|
|
178
|
-
//
|
|
179
|
-
//
|
|
180
|
-
//
|
|
181
|
-
//
|
|
182
|
-
//
|
|
217
|
+
// `useRelease` decides where Connect comes from, for this module AND —
|
|
218
|
+
// via config.gradle, which declares the same switch in the consuming
|
|
219
|
+
// app — for the APK. The two must agree: this module compiles against
|
|
220
|
+
// whatever it resolves here, while the app packages what the app
|
|
221
|
+
// resolves, and a bridge compiled against a newer Connect than the APK
|
|
222
|
+
// carries fails at runtime with NoSuchMethodError, not at build time.
|
|
183
223
|
//
|
|
184
|
-
//
|
|
185
|
-
//
|
|
186
|
-
//
|
|
187
|
-
//
|
|
188
|
-
// Verified: with this entry declared LAST, after mavenCentral(),
|
|
189
|
-
// `connect:+` still resolves to the newest beta here rather than to
|
|
190
|
-
// Central's older newest. Order only decides which repository serves a
|
|
191
|
-
// PINNED version that both carry, where the artifact is identical
|
|
192
|
-
// anyway — it is kept first so a pin on a beta is served by the tree
|
|
193
|
-
// that is authoritative for betas.
|
|
224
|
+
// useRelease: false (default) — the GitHub-hosted Maven tree, where
|
|
225
|
+
// Connect betas publish (they do not reach Maven Central). Central
|
|
226
|
+
// is still consulted for versions this tree does not carry.
|
|
227
|
+
// useRelease: true — Maven Central only, betas rejected below.
|
|
194
228
|
//
|
|
195
|
-
//
|
|
196
|
-
//
|
|
197
|
-
//
|
|
198
|
-
//
|
|
199
|
-
//
|
|
229
|
+
// `connect` resolves through a dynamic version, so Gradle queries
|
|
230
|
+
// every declared repository and takes the newest match across all of
|
|
231
|
+
// them; position only matters for a pinned version both carry, where
|
|
232
|
+
// the artifact is identical. The content filter is not cosmetic:
|
|
233
|
+
// without it every dependency in the build would hit
|
|
234
|
+
// raw.githubusercontent and 404 before falling through.
|
|
200
235
|
//
|
|
201
236
|
// Declared only here, not also in the top-level `repositories` block:
|
|
202
237
|
// `allprojects` covers this project too, so both would feed the same
|
|
203
238
|
// resolution and the pair could drift.
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
239
|
+
if (!acousticUseRelease) {
|
|
240
|
+
maven {
|
|
241
|
+
url = "https://raw.githubusercontent.com/go-acoustic/Android_Maven/master"
|
|
242
|
+
content { includeGroup "io.github.go-acoustic" }
|
|
243
|
+
}
|
|
207
244
|
}
|
|
208
245
|
|
|
209
246
|
mavenCentral()
|
|
@@ -216,23 +253,22 @@ allprojects {
|
|
|
216
253
|
}
|
|
217
254
|
}
|
|
218
255
|
|
|
219
|
-
//
|
|
220
|
-
//
|
|
221
|
-
//
|
|
222
|
-
//
|
|
223
|
-
//
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
println "AcousticConnectRN: PushEnabled=$acousticPushEnabled, artifact=io.github.go-acoustic:$acousticArtifact:$acousticVersion"
|
|
256
|
+
// Same beta rejection config.gradle applies to the app, so with
|
|
257
|
+
// `useRelease: true` this module cannot compile against a beta either — a
|
|
258
|
+
// beta reached through the Sonatype staging repo or an `AndroidVersion` pin
|
|
259
|
+
// fails resolution here instead of diverging from the APK. eocore is exempt:
|
|
260
|
+
// its release line carries a `-beta` suffix (see config.gradle).
|
|
261
|
+
if (acousticUseRelease) {
|
|
262
|
+
configurations.all {
|
|
263
|
+
resolutionStrategy.componentSelection.all { selection ->
|
|
264
|
+
if (selection.candidate.group == 'io.github.go-acoustic' &&
|
|
265
|
+
selection.candidate.module != 'eocore' &&
|
|
266
|
+
selection.candidate.version.endsWith('-beta')) {
|
|
267
|
+
selection.reject("useRelease is true — rejecting beta version ${selection.candidate.version}")
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
236
272
|
|
|
237
273
|
dependencies {
|
|
238
274
|
// Provided by the host app at runtime — compileOnly avoids version conflicts
|
|
@@ -284,18 +320,33 @@ dependencies {
|
|
|
284
320
|
// to releases that lack APIs this bridge calls; upper bound prevents a
|
|
285
321
|
// future 12.x major (with potentially breaking changes) from being picked
|
|
286
322
|
// up automatically by Gradle's `+` resolver. Both halves are `strictly` so
|
|
287
|
-
//
|
|
288
|
-
//
|
|
289
|
-
//
|
|
323
|
+
// nothing else in the build can move Connect outside them. A
|
|
324
|
+
// `Connect.AndroidVersion` outside the range fails at configuration time
|
|
325
|
+
// (the check after the config read above), not here. Constraints are declared for both artifact
|
|
290
326
|
// names — Gradle only enforces the one that resolves into the graph, so
|
|
291
327
|
// the unused entry is inert.
|
|
328
|
+
//
|
|
329
|
+
// An `AndroidVersion` pin is applied to BOTH constraints as `prefer`. With
|
|
330
|
+
// push enabled the only direct dependency is `connect-push-fcm`, so the pin
|
|
331
|
+
// used to reach `connect` only through that artifact's POM — and a strict
|
|
332
|
+
// range is itself a request that resolves to the newest version in range,
|
|
333
|
+
// so it outranked the transitive pin and floated `connect` past it
|
|
334
|
+
// (`connect-push-fcm:11.1.5-beta` shipped with `connect:11.1.6-beta`).
|
|
335
|
+
// `prefer` inside the strict range keeps the range as the hard bound and
|
|
336
|
+
// makes the pin the version chosen within it.
|
|
292
337
|
constraints {
|
|
293
338
|
implementation('io.github.go-acoustic:connect') {
|
|
294
|
-
version {
|
|
339
|
+
version {
|
|
340
|
+
strictly acousticSupportedRange
|
|
341
|
+
if (!acousticVersionPin.isEmpty()) { prefer acousticVersionPin }
|
|
342
|
+
}
|
|
295
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.'
|
|
296
344
|
}
|
|
297
345
|
implementation('io.github.go-acoustic:connect-push-fcm') {
|
|
298
|
-
version {
|
|
346
|
+
version {
|
|
347
|
+
strictly acousticSupportedRange
|
|
348
|
+
if (!acousticVersionPin.isEmpty()) { prefer acousticVersionPin }
|
|
349
|
+
}
|
|
299
350
|
because 'connect-push-fcm tracks the same release line as the core connect artifact.'
|
|
300
351
|
}
|
|
301
352
|
}
|
package/android/config.gradle
CHANGED
|
@@ -425,13 +425,100 @@ def updateEOCoreAdvConfig(mp, json) {
|
|
|
425
425
|
println("Updating:$eocoreAdvConfigPath")
|
|
426
426
|
}
|
|
427
427
|
|
|
428
|
+
// With `useRelease: false`, the app has to resolve Connect from the beta tree
|
|
429
|
+
// too. The SDK module's android/build.gradle declares it for the module's own
|
|
430
|
+
// classpath, but the APK packages what *this* project — the app — resolves,
|
|
431
|
+
// and an app that cannot see the tree silently gets Central's newest release
|
|
432
|
+
// instead: a different Connect from the one the bridge was compiled against,
|
|
433
|
+
// which fails at runtime with NoSuchMethodError. This script is applied from
|
|
434
|
+
// the app's build.gradle, so the repository declared here is the app's.
|
|
435
|
+
//
|
|
436
|
+
// Apps that centralise repositories in settings.gradle cannot take a project
|
|
437
|
+
// repository: FAIL_ON_PROJECT_REPOS fails the build on one, PREFER_SETTINGS
|
|
438
|
+
// ignores it — for every project, the SDK module included, so the two still
|
|
439
|
+
// agree (on Central). For those, say what to add rather than fail or stay silent.
|
|
440
|
+
def addBetaRepository(proj) {
|
|
441
|
+
// The GitHub-hosted Maven tree Connect betas publish to. They do not reach
|
|
442
|
+
// Maven Central, which carries release versions only. Local to the method:
|
|
443
|
+
// a script-level `def` is not visible inside script methods.
|
|
444
|
+
def ACOUSTIC_BETA_REPO_URL = "https://raw.githubusercontent.com/go-acoustic/Android_Maven/master"
|
|
445
|
+
def mode = null
|
|
446
|
+
def inSettings = false
|
|
447
|
+
try {
|
|
448
|
+
def drm = proj.gradle.settings.dependencyResolutionManagement
|
|
449
|
+
mode = drm.repositoriesMode.get().toString()
|
|
450
|
+
inSettings = drm.repositories.any { repo ->
|
|
451
|
+
repo.hasProperty('url') && repo.url.toString().startsWith(ACOUSTIC_BETA_REPO_URL)
|
|
452
|
+
}
|
|
453
|
+
} catch (Throwable ignored) {
|
|
454
|
+
// Older Gradle, or no settings-level repository management: nothing to respect.
|
|
455
|
+
}
|
|
456
|
+
if (inSettings) {
|
|
457
|
+
println("AcousticConnect: useRelease is false — Connect beta repository already declared in settings.gradle")
|
|
458
|
+
return
|
|
459
|
+
}
|
|
460
|
+
if (mode == "FAIL_ON_PROJECT_REPOS" || mode == "PREFER_SETTINGS") {
|
|
461
|
+
println("WARNING: AcousticConnect: useRelease is false, but settings.gradle sets repositoriesMode " +
|
|
462
|
+
"$mode, so the Connect beta repository cannot be added to the app. Add it to " +
|
|
463
|
+
"dependencyResolutionManagement.repositories in settings.gradle:\n" +
|
|
464
|
+
" maven {\n" +
|
|
465
|
+
" url = \"$ACOUSTIC_BETA_REPO_URL\"\n" +
|
|
466
|
+
" content { includeGroup \"io.github.go-acoustic\" }\n" +
|
|
467
|
+
" }\n" +
|
|
468
|
+
"Without it Connect resolves from Maven Central only — for the app and this SDK alike, since " +
|
|
469
|
+
"settings repositories replace project ones — so betas are not used. Or set useRelease to " +
|
|
470
|
+
"true to make that explicit.")
|
|
471
|
+
return
|
|
472
|
+
}
|
|
473
|
+
proj.repositories {
|
|
474
|
+
maven {
|
|
475
|
+
url = ACOUSTIC_BETA_REPO_URL
|
|
476
|
+
content { includeGroup "io.github.go-acoustic" }
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
println("AcousticConnect: useRelease is false — resolving Connect from $ACOUSTIC_BETA_REPO_URL")
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// With `useRelease: true`, reject every go-acoustic beta in the app's resolution.
|
|
483
|
+
// android/build.gradle applies the same rule to the SDK module.
|
|
484
|
+
def rejectBetaVersions(proj) {
|
|
485
|
+
proj.configurations.all {
|
|
486
|
+
resolutionStrategy {
|
|
487
|
+
componentSelection {
|
|
488
|
+
all { selection ->
|
|
489
|
+
// Exempt the eocore artifact from beta rejection.
|
|
490
|
+
// Full Maven coordinate: io.github.go-acoustic:eocore
|
|
491
|
+
// (verified on Maven Central — the POM declares
|
|
492
|
+
// <groupId>io.github.go-acoustic</groupId> +
|
|
493
|
+
// <artifactId>eocore</artifactId>, so candidate.module is
|
|
494
|
+
// literally 'eocore').
|
|
495
|
+
// Unlike the other go-acoustic artifacts, eocore's RELEASE
|
|
496
|
+
// line carries a `-beta` suffix — Maven Central lists
|
|
497
|
+
// 2.1.24-beta as <release>/<latest>, and the release
|
|
498
|
+
// connect:11.0.13 → tealeaf:10.4.28 chain pins eocore
|
|
499
|
+
// transitively to that version. Without this carve-out the
|
|
500
|
+
// blanket `-beta` reject would drop a legitimate release
|
|
501
|
+
// artifact and break every useRelease=true build.
|
|
502
|
+
if (selection.candidate.group == 'io.github.go-acoustic' &&
|
|
503
|
+
selection.candidate.module != 'eocore' &&
|
|
504
|
+
selection.candidate.version.endsWith('-beta')) {
|
|
505
|
+
selection.reject("useRelease is true — rejecting beta version ${selection.candidate.version}")
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
|
|
428
513
|
def updateGradle(mp, json){
|
|
429
514
|
def buildGradlePath = "$mp/build.gradle"
|
|
430
515
|
def buildGradleFile = new File(buildGradlePath)
|
|
431
516
|
def updatedBuildGradle = buildGradleFile.text
|
|
432
517
|
|
|
433
518
|
if (json != null && json.Connect) {
|
|
434
|
-
|
|
519
|
+
// Parsed as android/build.gradle parses it, so the module and the app agree on
|
|
520
|
+
// the value even when it is written as a string ("false" is truthy in Groovy).
|
|
521
|
+
def useRelease = (json.Connect.useRelease ?: false).toString().toBoolean()
|
|
435
522
|
|
|
436
523
|
if(json.Connect.AndroidVersion != null) {
|
|
437
524
|
def connectSDK = "io.github.go-acoustic:connect:"
|
|
@@ -443,33 +530,11 @@ def updateGradle(mp, json){
|
|
|
443
530
|
updatedBuildGradle = updatedBuildGradle.replaceAll(~/io.github.go-acoustic:connect:(\+|\d+\.\d+\.\d+(-beta)?)/, connectSDK)
|
|
444
531
|
}
|
|
445
532
|
|
|
533
|
+
// Exactly one of the two per build: release-only resolution, or the beta tree.
|
|
446
534
|
if (useRelease) {
|
|
447
|
-
project
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
all { selection ->
|
|
451
|
-
// Exempt the eocore artifact from beta rejection.
|
|
452
|
-
// Full Maven coordinate: io.github.go-acoustic:eocore
|
|
453
|
-
// (verified on Maven Central — the POM declares
|
|
454
|
-
// <groupId>io.github.go-acoustic</groupId> +
|
|
455
|
-
// <artifactId>eocore</artifactId>, so candidate.module is
|
|
456
|
-
// literally 'eocore').
|
|
457
|
-
// Unlike the other go-acoustic artifacts, eocore's RELEASE
|
|
458
|
-
// line carries a `-beta` suffix — Maven Central lists
|
|
459
|
-
// 2.1.24-beta as <release>/<latest>, and the release
|
|
460
|
-
// connect:11.0.13 → tealeaf:10.4.28 chain pins eocore
|
|
461
|
-
// transitively to that version. Without this carve-out the
|
|
462
|
-
// blanket `-beta` reject would drop a legitimate release
|
|
463
|
-
// artifact and break every useRelease=true build.
|
|
464
|
-
if (selection.candidate.group == 'io.github.go-acoustic' &&
|
|
465
|
-
selection.candidate.module != 'eocore' &&
|
|
466
|
-
selection.candidate.version.endsWith('-beta')) {
|
|
467
|
-
selection.reject("useRelease is true — rejecting beta version ${selection.candidate.version}")
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
}
|
|
535
|
+
rejectBetaVersions(project)
|
|
536
|
+
} else {
|
|
537
|
+
addBetaRepository(project)
|
|
473
538
|
}
|
|
474
539
|
}
|
|
475
540
|
|
|
@@ -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
|
-
|
|
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
|
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.
|
|
231
|
+
"version": "19.1.6",
|
|
232
232
|
"workspaces": [
|
|
233
233
|
"example",
|
|
234
234
|
"Examples/bare-workflow"
|