Conversation
nan-li
force-pushed
the
nan/sdk-5313
branch
2 times, most recently
from
September 24, 2026 01:05
3d63c3c to
9d03890
Compare
…ain queue addTriggers, removeTriggers, clearTriggers and unpausing read and mutate the controller's message collections on the caller's thread, while the fetch completion and the dismiss path write them on the main queue. A wrapper that calls these off main, such as React Native, can crash inside evaluateRedisplayedInAppMessages when the two overlap. Hop the bodies onto the main queue when called off it and run them inline when already there. The pause flag is still written before the hop so the getter reflects the call immediately, and clearTriggers reads the keys on the main queue so a trigger queued just ahead of it is cleared too. The pause branch now checks isInAppMessageShowing on the main queue, and messageIsNotActive deletes the message on the main queue as well, since the HTML request failure that reaches it arrives on the session queue.
nan-li
force-pushed
the
nan/sdk-5313
branch
from
September 24, 2026 15:20
9d03890 to
02a19e5
Compare
…dismiss Pausing queued the showing check itself on the main queue, so a pause with nothing showing followed by a message or preview in the same run-loop turn dismissed that message before it rendered and marked it seen. Run the check through the main-queue helper instead, so a main-thread caller checks at call time as before and a background caller checks on main, and keep only the dismiss deferred. The preview-on-pause integration test now drains the main queue before its assertion so this cannot regress.
nan-li
marked this pull request as ready for review
September 24, 2026 16:29
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
One Line Summary
Run in-app trigger changes and pause evaluation on the main queue, where the message state already lives. SDK-5313, fixes #1753.
Details
Motivation
addTriggers,removeTriggers,clearTriggersand unpausing evaluated messages on the calling thread. They readmessages,redisplayedInAppMessagesandearlySessionTriggerswithout a lock. Meanwhile, the fetch completion and dismiss path write to the same state on the main queue.React Native calls these methods from a background queue. If the operations overlap, the app crashes in
evaluateRedisplayedInAppMessageswithEXC_BAD_ACCESS.Scope
runOnMainQueue:method inOSMessagingControllerruns a block inline on the main thread or dispatches it asynchronously from any other thread.addTriggers:,removeTriggersForKeys:andclearTriggersrun their bodies through the helper.clearTriggersreads the keys inside the block, so it also clears a trigger queued immediately before it.setInAppMessagingPaused:still sets the flag synchronously, which meanspausedreads back immediately. The evaluation runs through the helper, and so does the check for a showing message before a dismiss is queued. A main-thread caller runs that check at call time, as before.messageIsNotActive:deletes the message inside the main-queue block it already used for the window cleanup. The HTML request fails on the session queue, so the delete used to replacemessagesoff main too.Testing
Unit testing
TriggerThreadingTestsseeds one message with redisplay state, calls each entry point from a global queue while holding the main thread, then drains the main queue. The add and remove tests verify that the redisplay check and the trigger controller aren't touched on the calling thread and run on the main thread afterward. The clear test verifies the same for the trigger controller and that a trigger queued from the same background call just before it is removed. The unpause test verifies the flag reads back immediately and that evaluation runs on the main thread. All four fail without this fix.testPreviewIAMIsDisplayedOnPausenow drains the main queue before its assertion, so a pause that wrongly queues a dismiss with nothing showing fails it.Manual testing
The
OneSignalInAppMessagesTestsbundle ran locally on an iPhone 17 Pro simulator with Xcode 27. Not run on a device.Affected code checklist
Checklist
Overview
Testing
Final pass
🤖 Generated with Claude Code