diff --git a/CHANGELOG.md b/CHANGELOG.md index c0673d32c..093632a2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - The bridge now displays the message on the main thread, matching `setAutoDisplayPaused` and the iOS SDK. It was calling the native in-app displayer on the React Native native-modules thread, which crashes when the current activity is not a `FragmentActivity` (for example a Jetpack Compose `ComponentActivity`). - The promise now rejects when the message id is empty or no longer in the native queue instead of crashing with a `NullPointerException`. - Dismissing a message by tapping outside it now resolves the promise with `null` instead of crashing on `url.toString()`. +- `Iterable.inAppManager.showMessage` now rejects on iOS when the message id is empty or no longer in the native queue, matching Android. Previously the promise never settled. ### Updates diff --git a/ios/RNIterableAPI/ReactIterableAPI.swift b/ios/RNIterableAPI/ReactIterableAPI.swift index a216a0e69..639d09c73 100644 --- a/ios/RNIterableAPI/ReactIterableAPI.swift +++ b/ios/RNIterableAPI/ReactIterableAPI.swift @@ -384,8 +384,16 @@ import React rejecter: RCTPromiseRejectBlock ) { ITBInfo() + guard !messageId.isEmpty else { + ITBError("messageId is null or empty") + rejecter("", "messageId is null or empty", NSError(domain: "", code: 0, userInfo: nil)) + return + } guard let message = IterableAPI.inAppManager.getMessage(withId: messageId) else { ITBError("Could not find message with id: \(messageId)") + rejecter( + "", "Could not find message with id: \(messageId)", + NSError(domain: "", code: 0, userInfo: nil)) return } IterableAPI.inAppManager.show(message: message, consume: consume) { (url) in diff --git a/src/__tests__/IterableInApp.test.ts b/src/__tests__/IterableInApp.test.ts index bddb3f3f9..a1e9472c3 100644 --- a/src/__tests__/IterableInApp.test.ts +++ b/src/__tests__/IterableInApp.test.ts @@ -228,6 +228,25 @@ describe('Iterable In App', () => { }); }); + test('showMessage_nativeRejects_propagatesRejectionToJs', async () => { + // JS contract only: the native rejection itself is implemented in the iOS and Android bridges + // GIVEN an in-app message that is no longer in the native queue + const message: IterableInAppMessage = IterableInAppMessage.fromDict({ + messageId: 'message1', + campaignId: 1234, + trigger: { type: IterableInAppTriggerType.immediate }, + }); + const error = new Error('Could not find message with id: message1'); + + // WHEN the native module rejects the call + MockRNIterableAPI.showMessage.mockRejectedValueOnce(error); + + // THEN Iterable.inAppManager.showMessage rejects with the native error + await expect( + Iterable.inAppManager?.showMessage(message, true) + ).rejects.toBe(error); + }); + test('removeMessage_params_methodCalledWithParams', () => { // GIVEN an in-app message const messageDict = {