fix: [SDK-4814] Live Activities Confirmed Receipts exceeding Delivered count#1681
Open
nan-li wants to merge 4 commits into
Open
fix: [SDK-4814] Live Activities Confirmed Receipts exceeding Delivered count#1681nan-li wants to merge 4 commits into
nan-li wants to merge 4 commits into
Conversation
…red content Read the notificationId from the content yielded by this contentUpdates iteration instead of activity.content (the latest snapshot), which can advance while the loop is suspended and mis-attribute the receipt to a different update. Co-authored-by: Cursor <cursoragent@cursor.com>
A receive receipt for a notificationId was re-sent on later content updates or app relaunches once the prior request was forgotten on success, inflating Confirmed Receipts above Delivered. Track sent notificationIds in an in-memory set, claimed at enqueue, so each is reported at most once per session, and shorten the unsent-receipt retry window from 30 to 7 days. Co-authored-by: Cursor <cursoragent@cursor.com>
…unches The in-memory dedup set did not survive app relaunches, so when ActivityKit re-emits an active activity's content on launch the same notificationId was reported again. Keep sent receipts in the persisted cache as dedup markers (shouldForgetWhenSuccessful = false); supersedes == false drops the re-send and pollPendingRequests skips successful entries, making the cache the single source of truth. Remove the redundant in-memory set and shorten the receive- receipts cache TTL from 7 to 3 days. Adds a relaunch regression test. Co-authored-by: Cursor <cursoragent@cursor.com>
The relaunch regression test pushed OSLiveActivitiesExecutorTests over SwiftLint's type_body_length error threshold, failing the CI lint step. Collapse the repeated per-test subscribed-user setup into a single helper, which removes the duplication and keeps the class body under the limit. Co-authored-by: Cursor <cursoragent@cursor.com>
Contributor
Author
|
Ran multi-model-review with GPT5.5, Opus 4.8, Codex |
fadi-george
approved these changes
Jun 30, 2026
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
Stop Live Activities from reporting a receive receipt more than once per device, which inflated the dashboard's Confirmed Receipts count above Delivered.
Details
Reproduced this manually by starting a live activity on the device, opening the app, swiping it away, reopening the app… It looks like ActivityKit re-emits content updates on app relaunches even when the content of the live activity does not change. The SDK had been treating each update as a separate message and was not deduplicating.
Motivation
For Live Activities, the dashboard's Confirmed Receipts count could exceed Delivered (SDK-4814). A receive receipt for a given
notificationIdwas reported to OneSignal more than once per device because:activity.content.state) instead of the content actually delivered in that update, so a burst of content updates could mis-attribute thenotificationId.Scope
content.stateof each update rather than the liveactivity.contentsnapshot.shouldForgetWhenSuccessful = false) as markers, so both in-session re-emits and post-relaunch re-emits for the samenotificationIdare suppressed.Testing
Unit testing
testReceiveReceiptsNotResentForSameNotificationIdAfterRelaunch, a regression test that sends a receipt through one executor, then has a second executor (simulating an app relaunch sharing the same persisted cache) re-enqueue the samenotificationIdand verifies it is not re-sent.testReceiveReceiptsWithSuccessfulRequestandtestReceiveReceiptsRequestNotExecutedWithSameNotificationIdto assert the sent receipt is retained in the cache as a dedup marker (requestSuccessful == true) rather than removed.Manual testing
On-device, with a Live Activity on the Lock Screen, swiped the app away to terminate it and reopened it — the path where ActivityKit re-emits the active activity's current content on launch. Before the fix this sent a duplicate Confirmed Receipt for the same
notificationId; after the fix the duplicate no longer reproduces.Affected code checklist
Checklist
Overview
Testing
Final pass
Made with Cursor