Conversation
Sample app builds 📱Below you will find the list of the latest versions of the sample apps. |
SDK binary size reports 📊SDK binary size of this PRSDK binary size diff report vs. main branch |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #980 +/- ##
==========================================
- Coverage 68.33% 68.14% -0.19%
==========================================
Files 182 182
Lines 9420 9524 +104
==========================================
+ Hits 6437 6490 +53
- Misses 2983 3034 +51 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
mrehan27
left a comment
There was a problem hiding this comment.
Looks good overall. Just two things before we merge, CI is failing and needs to be fixed if relevant (or may be rerun). And there's no test coverage for the new code, I believe we should add tests for both Reset Event and Lifecycle handling before merging.
| // Pass the new anonymous ID with the reset event so in-app messaging | ||
| // can continue fetching messages for the anonymous user after reset. | ||
| let newAnonymousId = analytics?.anonymousId | ||
| eventBusHandler.postEvent(ResetEvent(newAnonymousId: newAnonymousId)) |
There was a problem hiding this comment.
Shouldn't we also add a test to verify this?
| // MARK: - App Lifecycle & SSE State Handling | ||
|
|
||
| private extension Gist { | ||
| func setupLifecycleObservers() { |
There was a problem hiding this comment.
Can we add some tests for these?
| if state.shouldUseSse { | ||
| logger.logWithModuleTag("Gist: SSE enabled for identified user - stopping polling timer", level: .info) | ||
| invalidateTimer() | ||
| } else if !state.useSse { |
There was a problem hiding this comment.
I understand this might be outside the scope of this PR and coming from earlier changes, but looking through the code here, shouldUseSse and useSse flags are quite confusing. It would be nice to refactor them to more descriptive names in a separate PR so the expectations are clearer for callers like this one.
This PR solves two issues
Issue: In-App Messaging Fails After
clearIdentify()The issue affects Android and iOS
What Happens
clearIdentify publishes a "reset" event, then generates a new anonymous ID
In-app messaging receives the reset event and clears both userId and anonymousIdThe new anonymous ID is never communicated to in-app messagingAPI requests fail with 400 because no user token is available
Root Cause
Event ordering problem: The reset event is published before the new anonymous ID is generated, and no follow-up event notifies in-app messaging about the new anonymous ID.
Expected vs Actual
Expected: After reset, in-app messaging should use the new anonymous IDActual: In-app messaging has no user token at all, causing 400 errors
The issue only affects the session where the user was identified and then cleared. Once app is killed and re-launched, anonymous messages work again.
Problem: Polling Timer Continues Running in Background
This only affects iOS.
Problem: The polling timer for fetching in-app messages doesn't stop when the app goes to background—it only skips the actual network fetch.
Location: Gist.swift - setupPollingAndFetch() method (line 235) and fetchUserMessages() method (line 262)
What happens:
When polling is active, a repeating Timer.scheduledTimer fires every N seconds
If the user backgrounds the app, the timer continues firing
Each timer fire wakes the main thread, checks applicationState != .background, logs a skip message, and returnsThe timer never actually stops until the app is terminated or SSE takes over
Root cause: The Gist class has no lifecycle observers (background/foreground) to pause and resume the polling timer. It only performs a runtime check inside fetchUserMessages() to skip the work.
Impact:
Unnecessary CPU/battery drain from repeated timer fires in background
Log spam: "Application in background, skipping queue check" logged every poll interval
Potential timer drift/coalescing issues when iOS manages background app state
Inconsistent with SseLifecycleManager which properly stops SSE connections on backgroundContrast with SSE: The SseLifecycleManager correctly observes didEnterBackgroundNotification and willEnterForegroundNotification to stop/start the SSE connection, but the polling timer in Gist lacks equivalent handling
Note
Medium Risk
Touches in-app messaging state/reset flow and iOS app-lifecycle timer behavior; mistakes could cause missed message fetches or incorrect identity usage, but changes are localized and guarded.
Overview
Fixes in-app messaging after
clearIdentify()by extendingResetEventwith an optionalnewAnonymousId, publishing it from the DataPipeline reset hook, and re-seeding Gist’s anonymous identity after reset when provided.Improves iOS in-app polling efficiency by adding foreground/background lifecycle observers to pause/resume the polling timer, and adds a background-state guard when scheduling the timer to avoid race conditions (SSE still takes precedence when active).
Written by Cursor Bugbot for commit 8106cb7. This will update automatically on new commits. Configure here.