Skip to content

feat: [SDK-5391] send a session end when a sessions API session ends - #1772

Closed
fadi-george wants to merge 1 commit into
fadi/sdk-5383from
fadi/SDK-5391-ios-session-end
Closed

fadi-george wants to merge 1 commit into
fadi/sdk-5383from
fadi/SDK-5391-ios-session-end

Conversation

@fadi-george

@fadi-george fadi-george commented Oct 9, 2026 •

Copy link
Copy Markdown
Collaborator

Description

One Line Summary

When the sessions API flag is on, enqueue a session end with end_time and the final duration when the next session starts.

Details

Motivation

Part of the iOS SDK Sessions stack (SDK-5391). Ended sessions need a PATCH /apps/{app_id}/sessions/{session_id} with end_time and the final cumulative duration_seconds. Based on #1770. Android counterpart: OneSignal/OneSignal-Android-SDK#2781.

Scope

  • iOS has no background job to end a session, so a session ends when the next one starts (resume after more than 30 s, or cold start). OSSessionService.startNewSession takes the previous record under the same lock that replaces it, then enqueues its end before the new session's create.
  • end_time is the last time the app left the foreground, saved in a new optional OSSessionRecord.lastUnfocusTime (records stored by earlier versions still decode). A session killed before its first unfocus ends at its start time.
  • Duration is the record's final activeDuration.
  • End replaces unsent updates for the session, carries the highest queued duration, and blocks later updates, all through the existing coalescing in OSSessionOperationExecutor. No executor changes.
  • The previous record's IDs are filled before it is ended (the user provider is now set first). Any still missing are resolved from its identity model at send time. A session whose user is no longer loaded, such as the user before a login in an earlier process, is sent with its pinned onesignal_id.
  • Only sessions whose captured usesSessionsApi is true are ended this way. Flag off: nothing new is sent, and the legacy focus-time processors are unchanged.

Things to note

  • The end is sent at the next app open, which can be long after the session ended.
  • Time from the last foreground visit before a kill is not counted, same as before. The heartbeat and background checkpoint tickets cover that.
  • end_time and start_time are both wall clock, so a clock moved back mid-session could produce end_time before start_time.

Testing

Unit testing

Added OSSessionServiceTests cases: the end's time and final duration, ordering before the next session's create, ending a session left by an earlier process before its user was created with its IDs, end time after a kill while focused, and no end for a legacy session. OSSessionOperationExecutorTests covers a session whose user is no longer loaded; end replacing and blocking updates is covered there from #1768. OSSessionServiceTests checks lastUnfocusTime is recorded, survives decoding of old records, and is not set by an unfocus in a new process.

The full UnitTestApp scheme passes locally, apart from the UserConcurrencyTests.testSubscriptionExecutorConcurrency flake. SwiftLint reports no new violation types.

Manual testing

Not tested on device yet; end-to-end verification is planned in the iOS rollout and QA ticket.

Affected code checklist

  • Notifications
    • Display
    • Open
    • Push Processing
    • Confirm Deliveries
  • Outcomes
  • Sessions
  • In-App Messaging
  • REST API requests
  • Public API changes

Checklist

Overview

  • I have filled out all REQUIRED sections above
  • PR does one thing
  • Any Public API changes are explained in the PR details and conform to existing APIs

Testing

  • I have included test coverage for these changes, or explained why they are not needed
  • All automated tests pass, or I explained why that is not possible
  • I have personally tested this on my device, or explained why that is not possible

Final pass

  • Code is as readable as possible.
  • I have reviewed this PR myself, ensuring it meets each checklist item

🤖 Generated with Claude Code

@fadi-george
fadi-george added this pull request to stack #1771 October 9, 2026 22:55
@fadi-george
fadi-george force-pushed the fadi/SDK-5391-ios-session-end branch from c9c96f9 to 63f846c Compare October 9, 2026 22:56
… starts

OSSessionService.startNewSession enqueues the previous session's end through
the session user provider, before the new session's create, with end_time from
the new lastUnfocusTime (or the start time for a session killed in the
foreground) and the final duration. A session whose user is no longer loaded is
sent with its pinned onesignal_id.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@fadi-george
fadi-george force-pushed the fadi/SDK-5391-ios-session-end branch from 63f846c to 5815b71 Compare October 10, 2026 00:49

@abdulraqeeb33 abdulraqeeb33 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Approving.

The previous session is ended before the new create, and a user who is no longer loaded is restored from the pinned onesignal id. That stand-in lives in the in-memory identity repo.

A kill while focused still ends at startTime with duration 0. The heartbeat that covers this is #1773, and that branch is not based on this one, so the gap stays until it is rebased.

if let previous, previous.usesSessionsApi {
// A session killed before its first unfocus ends at its start. Its foreground time was
// never added to the duration either.
userProvider.enqueueSessionUpdate(previous, endTime: previous.lastUnfocusTime ?? previous.startTime)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

A kill while focused has no lastUnfocusTime, so this ends at startTime with duration 0. #1773 is the fix, and it is not based on this branch. Rebase that onto this before the gap is closed.

@fadi-george

Copy link
Copy Markdown
Collaborator Author

Closing, the session queue PR (#1768) was redone on top of #1775. Will reopen session start and end on top of it.

@fadi-george

Copy link
Copy Markdown
Collaborator Author

Replaced by #1782, rebased on the new session executor.

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.

3 participants