Skip to content

SDK-478 resolve init promise immediately on iOS SDK, matching Android SDK#869

Open
joaodordio wants to merge 2 commits into
masterfrom
fix/SDK-478-bridge-resolve-immediately
Open

SDK-478 resolve init promise immediately on iOS SDK, matching Android SDK#869
joaodordio wants to merge 2 commits into
masterfrom
fix/SDK-478-bridge-resolve-immediately

Conversation

@joaodordio
Copy link
Copy Markdown
Member

@joaodordio joaodordio commented May 21, 2026

🔹 JIRA Ticket(s) if any

✏️ Description

What this changes

The iOS bridge for Iterable.initialize now resolves the JS promise immediately after calling the native SDK's synchronous initializer, matching the behavior the Android bridge has had since day one. The native iOS SDK is fully usable the moment IterableAPI.initialize(...) returns; nothing about JS-side correctness needs the in-app message fetch to complete before the promise resolves.

Why

The promise contract was originally introduced in commit 4c357126 (Feb 2021) with the intent of letting JS callers await Iterable.initialize() to know that the SDK is ready. The implementation wired the promise to the iOS SDK's IterableAPI.initialize2(callback:) overload, which is documented in the iOS SDK as an internal staging/test endpoint override and whose callback fires when the first in-app messages fetch settles, not when the SDK is ready to use. This was a workable approximation in 2021 because the fetch typically settled in under 2s on healthy networks.

Two later changes made the gap between "SDK ready" and "first fetch settles" observable as a multi-second to multi-minute hang:

  • iOS SDK 6.6.6 (PR #997) added a synchronous keychain migration in start() that runs on the calling thread, which for cross-platform bridges is the main thread (DispatchQueue.main.async).
  • JWT-enabled customers exercise an authentication refresh path during init that can consume the full default retry budget (~60s).

Prior consolidation attempts include commit c07b5ccd (Oct 2025) which made exactly the change this PR makes but did not survive the merge to master, and commit f37cc10b (April 2026) which added a 10s timeout band-aid as part of SDK-392 but also did not survive to master.

This PR consolidates the correction: bridge calls sync IterableAPI.initialize(...) and resolves true immediately. Behavior now matches Android's RNIterableAPIModuleImpl.initializeWithApiKey which has done the same since its introduction. The internal initialize2 (endpoint override) path is preserved for staging/test callers but its callback is no longer waited on.

Compatibility

Documented usage in iterable-docs already shows Iterable.initialize('<KEY>', config); without await, aligned with the new behavior. Any caller relying on the old wait semantics (the promise resolving only after the in-app fetch attempt) would have been exposed to an unbounded hang under SDK-478 conditions, so the likelihood of customer code depending on it is low. The behavior change is called out explicitly in the CHANGELOG and the release notes.

Tests

New SDK-478 contract-pin tests in src/core/classes/IterableApi.test.ts: initializeWithApiKey and initialize2WithApiKey both assert that the JS-side call resolves in under 50ms when the native mock resolves true, pinning the "pure passthrough, no additional await" contract.

Linked tickets

  • SDK-392 (the customer-facing hang report)
  • SDK-478 (validation work that surfaced the bridge contract issue)
  • SDK-294 (the iOS SDK keychain isolation that amplified the gap; iOS SDK side is being addressed in a separate PR in iterable-swift-sdk)

@github-actions
Copy link
Copy Markdown

Lines Statements Branches Functions
Coverage: 63%
63.07% (398/631) 40.07% (103/257) 61.5% (139/226)

@qltysh
Copy link
Copy Markdown

qltysh Bot commented May 21, 2026

Qlty


Coverage Impact

This PR will not change total coverage.

🚦 See full report on Qlty Cloud »

🛟 Help
  • Diff Coverage: Coverage for added or modified lines of code (excludes deleted files). Learn more.

  • Total Coverage: Coverage for the whole repository, calculated as the sum of all File Coverage. Learn more.

  • File Coverage: Covered Lines divided by Covered Lines plus Missed Lines. (Excludes non-executable lines including blank lines and comments.)

    • Indirect Changes: Changes to File Coverage for files that were not modified in this PR. Learn more.

@joaodordio joaodordio self-assigned this May 21, 2026
@joaodordio joaodordio marked this pull request as ready for review May 21, 2026 16:27
@joaodordio joaodordio requested review from lposen and sumeruchat May 21, 2026 17:10
@joaodordio
Copy link
Copy Markdown
Member Author

Note

Heads up to reviewers: the failing Analyze (java-kotlin) check on the CodeQL Advanced workflow is pre-existing on master and unrelated to this PR.

Evidence. The job has been red on every master push since 55ac386b (merge of #860, RN 0.79 upgrade, 2026-05-15). Latest master HEAD (de793ec, 2026-05-20) still fails the same way. The last successful run on master was e69c305c on 2026-03-24, before the RN 0.79 work landed.

Root cause. example/android/settings.gradle:3 calls autolinkLibrariesFromCommand() (the RN 0.79+ autolinking entry point), which shells out to npx @react-native-community/cli config from inside Gradle's settings evaluation. The Gradle process Run by CodeQL's build tracer doesn't inherit npx on its PATH, so the subprocess returns exit 127 ("command not found") and the build aborts.

Why it can't be from this PR. The diff is iOS-only - no Android files, no Gradle files, no workflow files touched. Reproduces verbatim on master without any of this PR's changes.

Suggested follow-up (separate PR). Either add an explicit actions/setup-node step right before the ./gradlew clean step in .github/workflows/codeql.yml, or swap the npx call in settings.gradle for the explicit node + cli path overload:

ex.autolinkLibrariesFromCommand([
  "node",
  "node_modules/@react-native-community/cli/build/bin.js",
  "config"
])

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant