Skip to content

Commit 1389b80

Browse files
committed
Merge branch 'master' into feature/SDK-518-increase-test-coverage
2 parents b442f92 + 9e6161d commit 1389b80

3 files changed

Lines changed: 66 additions & 10 deletions

File tree

CHANGELOG.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
## Unreleased
22

3+
### Fixes
4+
- Fixed the React Native bridge contract for `Iterable.initialize` on iOS.
5+
- The iOS bridge now calls the synchronous native initializer (`IterableAPI.initialize`) and resolves the JS promise immediately, matching the Android bridge which has always behaved this way.
6+
- Previously, the iOS bridge wired the JS promise to the `initialize2(callback:)` overload, whose callback fires when the first in-app messages fetch settles, not when the SDK is ready to use. That callback is an `InAppManager.start()` signal and has nothing to do with whether `IterableAPI.initialize` succeeded.
7+
- On iOS, `IterableAPI.initialize(...)` is synchronous, non-failable, and returns `Void`. The native SDK is fully usable the moment it returns. There is no init-error channel to await on the native side, and JS callers should not treat the promise as one. `await Iterable.initialize(...)` is supported for API symmetry but does not gate SDK readiness on any async work.
8+
- The user-visible symptom of the old contract was a multi-second to multi-minute hang on the JS promise under JWT or network friction, surfaced as an init failure even though the SDK had already initialized successfully. The error originated in the in-app messages fetch retry budget, not in initialization.
9+
310
### Updates
411

512
- Added `Iterable.registerDeviceToken(token)` to re-enable push for the current device.
@@ -13,7 +20,6 @@
1320
- Updated RN compatibility to 0.85
1421

1522
## 3.0.0
16-
1723
### Updates
1824

1925
- Added embedded messaging functionality. This includes the ability to:

ios/RNIterableAPI/ReactIterableAPI.swift

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -668,24 +668,40 @@ import React
668668
name: Notification.Name.iterableInboxChanged, object: nil)
669669

670670
DispatchQueue.main.async {
671-
IterableAPI.initialize2(
672-
apiKey: apiKey,
673-
launchOptions: launchOptions,
674-
config: iterableConfig,
675-
apiEndPointOverride: apiEndPointOverride
676-
) { result in
677-
resolver(result)
671+
// The native iOS SDK is fully usable the moment IterableAPI.initialize
672+
// returns. The legacy initialize2(callback:) overload fires its callback
673+
// only after inAppManager.start() resolves the first in-app messages
674+
// fetch, which can take 60s+ under any auth or network friction and
675+
// blocks the JS promise on a signal that does not actually represent
676+
// "SDK ready". Android's RNIterableAPIModuleImpl.initializeWithApiKey
677+
// resolves promise.resolve(true) immediately after sync init - this
678+
// brings iOS to parity. See SDK-478.
679+
if let apiEndPointOverride = apiEndPointOverride {
680+
IterableAPI.initialize2(
681+
apiKey: apiKey,
682+
launchOptions: launchOptions,
683+
config: iterableConfig,
684+
apiEndPointOverride: apiEndPointOverride
685+
)
686+
} else {
687+
IterableAPI.initialize(
688+
apiKey: apiKey,
689+
launchOptions: launchOptions,
690+
config: iterableConfig
691+
)
678692
}
679693

680694
IterableAPI.setDeviceAttribute(name: "reactNativeSDKVersion", value: version)
681-
695+
682696
// Add embedded update listener if any callback is present
683697
let onEmbeddedMessageUpdatePresent = configDict["onEmbeddedMessageUpdatePresent"] as? Bool ?? false
684698
let onEmbeddedMessagingDisabledPresent = configDict["onEmbeddedMessagingDisabledPresent"] as? Bool ?? false
685-
699+
686700
if onEmbeddedMessageUpdatePresent || onEmbeddedMessagingDisabledPresent {
687701
IterableAPI.embeddedManager.addUpdateListener(self)
688702
}
703+
704+
resolver(true)
689705
}
690706
}
691707

src/core/classes/IterableApi.test.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,24 @@ describe('IterableApi', () => {
7171
);
7272
expect(result).toBe(true);
7373
});
74+
75+
// SDK-478: the bridge contract is "resolve true immediately after calling
76+
// sync IterableAPI.initialize on the native side". The JS layer is a pure
77+
// passthrough. This pins that the JS code does not add an await on any
78+
// additional async work between the native call and the returned promise.
79+
// Regression guard for the 2021 contract drift that gated the JS promise
80+
// on the first in-app messages fetch (commit 4c357126).
81+
it('resolves promptly without awaiting any additional async work', async () => {
82+
MockRNIterableAPI.initializeWithApiKey.mockResolvedValueOnce(true);
83+
const startedAt = Date.now();
84+
const result = await IterableApi.initializeWithApiKey('test-api-key', {
85+
config: new IterableConfig(),
86+
version: '1.0.0',
87+
});
88+
const elapsedMs = Date.now() - startedAt;
89+
expect(result).toBe(true);
90+
expect(elapsedMs).toBeLessThan(250);
91+
});
7492
});
7593

7694
describe('initialize2WithApiKey', () => {
@@ -120,6 +138,22 @@ describe('IterableApi', () => {
120138
);
121139
expect(result).toBe(true);
122140
});
141+
142+
// SDK-478: same contract as initializeWithApiKey above. initialize2 is only
143+
// used for staging/test endpoint overrides; its JS-side behavior is still
144+
// "resolve immediately with whatever native returns" - no additional waits.
145+
it('resolves promptly without awaiting any additional async work', async () => {
146+
MockRNIterableAPI.initialize2WithApiKey.mockResolvedValueOnce(true);
147+
const startedAt = Date.now();
148+
const result = await IterableApi.initialize2WithApiKey('test-api-key', {
149+
config: new IterableConfig(),
150+
version: '1.0.0',
151+
apiEndPoint: 'https://api.staging.iterable.com',
152+
});
153+
const elapsedMs = Date.now() - startedAt;
154+
expect(result).toBe(true);
155+
expect(elapsedMs).toBeLessThan(250);
156+
});
123157
});
124158

125159
// ====================================================== //

0 commit comments

Comments
 (0)