Skip to content

[SDK-792] fix(ios): reject showMessage for an empty or unknown message id - #920

Open
joaodordio wants to merge 4 commits into
masterfrom
fix/SDK-792-ios-show-message-reject
Open

joaodordio wants to merge 4 commits into
masterfrom
fix/SDK-792-ios-show-message-reject

Conversation

@joaodordio

@joaodordio joaodordio commented Oct 8, 2026 •

Copy link
Copy Markdown
Member

📝 Summary

Reject showMessage on iOS for an empty or unknown message id, matching the Android bridge after #918.

🎟️ Jira Ticket: SDK-792

📖 Description

ReactIterableAPI.showMessage logged ITBError and returned without calling the resolver or rejecter when the message id was not in the native queue, so the JS promise never settled. An empty id was not checked at all.

It now rejects with the same code and messages as Android (messageId is null or empty, Could not find message with id: <id>), using the same rejecter pattern as getHtmlInAppContent in this file.

Stacked on #918. Branch is cut from #918 so the CHANGELOG lands under the same ## 3.2.1 section. The diff shrinks to three files once #918 merges.

🧪 How to test?

  • yarn test (adds showMessage_messageNotInNativeQueue_rejects, which pins the JS side of the contract)
  • Example app builds for the iOS simulator with Xcode 27 (xcodebuild ... -sdk iphonesimulator build, BUILD SUCCEEDED)
  • Manual: call Iterable.inAppManager.showMessage with a message that was consumed elsewhere, expect a rejection instead of a hanging promise

🧾 Changelog

Added under ## 3.2.1.

📚 Docs PR if applicable

Covered by SDK-793.

… compiles against native 3.10.1

The published iterableapi 3.10.1 AAR is compiled with Kotlin 1.9 without jvm-default,
so onEmbeddedMessagingSyncSucceeded and onEmbeddedMessagingSyncFailed are abstract
to Java implementers. RNIterableAPIModuleImpl did not override them, so the module
failed javac. Add no-op overrides that match the native defaults.
Iterable.inAppManager.showMessage called IterableInAppManager.showMessage on the React
Native native-modules thread. Since native Android 3.9.0, a host activity that is not a
FragmentActivity takes the Dialog path, whose LifecycleRegistry.addObserver call throws
IllegalStateException off the main thread and kills the process.

- Post the native call with UiThreadUtil.runOnUiThread, matching setAutoDisplayPaused
  and the native iOS SDK.
- Reject when the message id is missing instead of passing null into native (NPE).
- Resolve null when the message is dismissed without a URL instead of NPE on toString.
- Fix the empty messageId check, which used reference equality.

Adds the first JVM unit tests for the Android bridge (Robolectric + Mockito, versions
mirrored from iterable-android-sdk) and excludes them from the npm package.
…ge id

The iOS bridge logged and returned without settling the promise. Reject with the same
messages as the Android bridge.
@joaodordio
joaodordio requested a review from a team as a code owner October 8, 2026 17:21
@github-actions

github-actions Bot commented Oct 8, 2026 •

Copy link
Copy Markdown
Lines Statements Branches Functions
Coverage: 72%
71.92% (579/805) 61.22% (229/374) 67.18% (174/259)

@qltysh

qltysh Bot commented Oct 8, 2026

Copy link
Copy Markdown

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.

@qltysh

qltysh Bot commented Oct 8, 2026

Copy link
Copy Markdown

1 new issue

Tool Category Rule Count
qlty Structure Function with high complexity (count = 6): showMessage 1

}
});
}
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Function with high complexity (count = 6): showMessage [qlty:function-complexity]

Comment thread src/__tests__/IterableInApp.test.ts Outdated
Comment on lines +231 to +247
test('showMessage_messageNotInNativeQueue_rejects', async () => {
// 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);
});

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Jest test does not lock either reject message

What the spec says: SDK-792 asks for those two reject strings on iOS, and for Jest coverage in the mock layer if applicable. The strings themselves live in ReactIterableAPI.showMessage (~L387–397), which this test never calls.

Why it conflicts: The test passes for any rejection, including a wrong message or a revert of the Swift change. It does not check the empty-id string. That is allowed by “if applicable” — the mock cannot see the native queue — so this is not a missed acceptance criterion. The test name overclaims what it pins.

Suggested action: Either rename it so it only claims “a native rejection is propagated,” or add a mock-layer case whose expected message is messageId is null or empty. Do not treat this Jest file as proof of the iOS strings; those are in the Swift guards, matched to Android ~L290–296.

This branch has not been deployed

No deployments
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.

2 participants