Skip to content

feat: [SDK-5371] session model and sessions v2 feature flag - #1762

Merged
fadi-george merged 7 commits into
new-session-eventsfrom
fadi/sdk-5371
Oct 9, 2026
Merged

fadi-george merged 7 commits into
new-session-eventsfrom
fadi/sdk-5371

Conversation

@fadi-george

Copy link
Copy Markdown
Collaborator

Description

One Line Summary

Persist a per-session record that captures the sdk_sessions_v2_api_cutover flag and pinned user/subscription IDs at session start, and measure foreground time with a monotonic clock.

Details

Motivation

First iOS PR in the SDK Sessions stack (SDK-5368), matching Android OneSignal/OneSignal-Android-SDK#2774. Later PRs need a persisted per-session record that knows which path the session is on and which user/subscription it belongs to, even after login.

Scope

  • Bumps the OneSignal-KMP-SDK submodule to 3a4ce04 for FeatureFlag.SDK_SESSIONS_V2_API_CUTOVER (feat: [SDK-5369] add sdk_sessions_v2_api_cutover feature flag OneSignal-KMP-SDK#27), the same commit Android uses. It is on the KMP new-session-events branch, not main.
  • New OSSessionService in OneSignalOSCore with an OSSessionRecord: session UUID, start time, cumulative active duration, usesSessionsApi, pinned onesignalId and subscriptionId, and a serverSessionId slot. Persisted to UserDefaults under OSUD_SESSION_RECORD.
  • OneSignalUserManagerImpl.startNewSession() starts a new record. OneSignalTracker reports focus and unfocus.
  • The flag is read once at session start and fixed for the session.
  • Active duration uses CLOCK_MONOTONIC_RAW. The focus stamp is in memory only, so a reboot or a previous process can never produce a bogus interval.
  • Flag off: nothing reads the record, so behavior is unchanged.
  • No network calls.

Other

iOS has no local OneSignal IDs; onesignal_id is nil until the user is created. The record pins the identity model's local ID too, and fills in the OneSignal ID once the backend assigns it to that same user. A missing subscription ID is only filled in while that user is still current, since after a login the push subscription is created for the new user. Logins and user switches leave pinned IDs alone.

Testing

Unit testing

New OSSessionServiceTests: flag capture on/off, flag fixed mid-session, new session ID per session, IDs pinned after login, IDs filled in once the session's user is created (and not for another user's subscription), duration across focus cycles, unfocus without focus, and the record surviving a restart. swiftlint adds no new violations.

UnitTestApp_TestPlan_Reduced passes apart from intermittent failures in UserConcurrencyTests.testSubscriptionExecutorConcurrency and OneSignalUserTests, which pass on rerun and also fail intermittently on the base branch.

Manual testing

Not tested on a device. No user-facing behavior changes with the flag off.

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

Persist a per-session record that captures the sdk_sessions_v2_api_cutover
flag at session start, pins the OneSignal and push subscription IDs, and
measures foreground time with a monotonic clock. Nothing reads the record
while the flag is off.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Multi-model review (Claude Opus 5.5, GPT 5.6 Sol, Grok 4.7) of the session record + sdk_sessions_v2_api_cutover pin.

Act on

  • (3/3) startNewSession always sets focusedAt = now and never commits the open interval. Tracker calls onFocus first, then startNewSession: dispatches startNewSessionInternal onto main. That later overwrite drops the dispatch/startup slice from activeDuration. If the user backgrounds before the async block runs, onUnfocus persists time on the old record and this then replaces it with activeDuration == 0. Keep an existing foreground stamp, and commit now - focusedAt onto the record being replaced.
  • (3/3) Production fromInit:YES always replaces the only persisted slot without loading or finalizing it. testRecordSurvivesARestart constructs a second service and never goes through init, so it does not model a cold start. Later PRs that send this record need a handoff (resume vs close-then-replace) decided here.

Consider

  • (2/3) fillPinnedIds refuses a late subscriptionId once sessionIdentityModelId has moved, but createNewUser / identifyUser reuse the same push model. Init + login() before create-user returns can leave the first session with subscriptionId == nil forever, while a known ID at start stays pinned through login.
  • Non-atomic provider reads (model id, onesignal id, push id) can tear across a login.
  • If initialize runs after the first didBecomeActive, lastOnFocusWasToBackground stays YES, the first resign is ignored, and the next onFocus throws away the first visit.
  • handleAppIdChange: clears other unscoped keys but not OSUD_SESSION_RECORD.

Noted
Flag-freeze test may not flip an APP_STARTUP latch. Failed decode sets loaded and never retries. No integration test that UserManager / Tracker actually write the record.

Dismissed
Flag-off still writing a record (documented). currentRecord fill side effects (intentional).

Open in Web View Automation 

Sent by Cursor Automation: PR Reviews

Comment thread iOS_SDK/OneSignalSDK/OneSignalOSCore/Source/OSSessionService.swift Outdated
Comment thread iOS_SDK/OneSignalSDK/OneSignalOSCore/Source/OSSessionService.swift Outdated
@fadi-george
fadi-george requested a review from nan-li October 7, 2026 22:47
@fadi-george
fadi-george changed the base branch from main to new-sessions-events October 7, 2026 22:50
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@fadi-george fadi-george mentioned this pull request Oct 7, 2026
10 of 18 tasks
@fadi-george
fadi-george added this pull request to stack #1765 October 7, 2026 23:41
@fadi-george

Copy link
Copy Markdown
Collaborator Author

Addressed in b2c0925:

  • Provider reads now come from one snapshot of the current user (sessionCurrentUser).
  • handleAppIdChange: clears OSUD_SESSION_RECORD via resetAndClearStoredRecord.
  • Flag freeze test now asserts the feature manager actually flipped off.
  • Integration coverage for the User module wiring is in SessionRecordUserTests.

Not changing:

  • Init after the first didBecomeActive: the tracker ignores that first resign, so legacy focus time loses the same visit. Better fixed in the tracker separately.
  • Failed decode not retrying: a record that fails to decode stays corrupt, and the next session start replaces it.

@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.

The replies say these are fixed in b2c0925, but that commit is on #1763, not this branch. At this head the subscriptionId fill gap after identifyUser (inline), the focusedAt overwrite in startNewSession (line 141), and the record clear on app id change are still open. Please move b2c0925 here, or merge the two PRs.

Comment thread iOS_SDK/OneSignalSDK/OneSignalOSCore/Source/OSSessionService.swift Outdated
- Keep an open foreground interval when a session starts, so the visit that
  started it counts from focus and a background launch counts nothing.
- Read the current user's IDs from one snapshot.
- Fill in the subscription ID after a login that identified the same user.
- Clear the stored record on an app-id change.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@fadi-george

fadi-george commented Oct 8, 2026 •

Copy link
Copy Markdown
Collaborator Author

Thanks, I'd fixed the commits being on wrong branch

@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.

All three are fixed in f9e6376 and verified.

  1. Subscription id now fills in after identifyUser via the onesignal_id match, with a test.
  2. startNewSession leaves the focus stamp alone.
  3. handleAppIdChange clears the stored record.

Non-blocking, for the sender PR: resetAndClearStoredRecord drops the in-memory focusedAt, so an app id change while foregrounded skips the current visit until the next focus.

#1763 still carries b2c0925, so it needs a rebase onto this.

@nan-li nan-li 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.

One item worth addressing, inline on fillPinnedIds. The other two are a consider and a nit.

Comment thread iOS_SDK/OneSignalSDK/OneSignalOSCore/Source/OSSessionService.swift
Comment thread iOS_SDK/OneSignalSDK/OneSignalOSCore/Source/OSSessionService.swift
Comment thread iOS_SDK/OneSignalSDK/OneSignalOSCore/Source/OSSessionService.swift Outdated
fadi-george and others added 4 commits October 8, 2026 13:05
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The push subscription is carried across logins, so its ID is always this
device's. A login that conflicted before the anonymous user was created left
the session with no subscription ID, so it could never be sent.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@fadi-george
fadi-george requested a review from nan-li October 8, 2026 22:17

@nan-li nan-li 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.

Thanks for the work! LGTM

@fadi-george
fadi-george merged commit e2e1cfd into new-session-events Oct 9, 2026
10 of 12 checks passed
@fadi-george
fadi-george deleted the fadi/sdk-5371 branch October 9, 2026 19:15
fadi-george added a commit that referenced this pull request Oct 9, 2026
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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