Skip to content

feat: [SDK-5538] deferred managers that stand in before init - #2802

Draft
abdulraqeeb33 wants to merge 2 commits into
never-crash/2-kill-switchfrom
never-crash/3-deferred-managers
Draft

abdulraqeeb33 wants to merge 2 commits into
never-crash/2-kill-switchfrom
never-crash/3-deferred-managers

Conversation

@abdulraqeeb33

@abdulraqeeb33 abdulraqeeb33 commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Stack (base feat/never-crash, merged to main as one unit; split out of #2793):

  1. refactor: [SDK-5538] add SdkOperation names for init-gated entry points #2800 SdkOperation names
  2. feat: [SDK-5538] remote kill switch with a placeholder OneSignalImpV2 #2801 Kill switch with a placeholder OneSignalImpV2
  3. feat: [SDK-5538] deferred managers that stand in before init #2802 Deferred managers (this PR)
  4. fix: [SDK-5538] never crash on invalid init or calls before init #2803 Never-crash init
  5. test: [SDK-5538] guard stand-ins and the public API against drift #2828 Drift tests for the stand-ins and the public API

Description

One Line Summary

Deferred managers that stand in for User, Notifications, InAppMessages, Session, Location and the JWT listeners before init succeeds.

Details

Motivation

Part of SDK-5538. Today every accessor throws before init. PR 4 hands these out instead, so calls before init log rather than crash.

Scope

  • Every call re-resolves the real manager through a ManagerSource, so a reference captured before init (for example through dependency injection) works after init or after a retry.
  • Before init, registrations are queued and replayed in order, once: user and push subscription observers, permission observers, notification lifecycle and click listeners, IAM lifecycle and click listeners, JWT invalidated listeners, InAppMessages.paused and Location.isShared.
  • The queue does not grow on add and remove pairs: a removal cancels its queued add, and a setting keeps only its latest value. The paused and isShared getters return the queued value.
  • Adds are capped at 100 per manager, so a process whose init never succeeds can't grow the queue forever. Past the cap an add is logged as ignored. Removals and setting changes are never capped.
  • After replay, registrations and settings go to the manager already replayed into. A listener that calls back in during replay, or a registration after a later failure, is not dropped.
  • Other calls are logged by name and return a default. Suspend members (for example requestPermission) suspend on init instead of blocking.
  • A replayed call that throws is logged (Replaying 'X' threw; that queued call is lost) and the rest still replay. A failing pushSubscription lookup during replay is logged instead of failing init.
  • Not wired in yet; PR 4 does that. No behavior change.

Testing

Unit testing

DeferredManagersTests: for every manager, non-registration calls are ignored before ready and forward after; registrations queue and replay in order, once; a removal cancels its queued add; a setting keeps its latest value and reads back; suspend members await; a failing replayed call does not stop the rest; the queue is bounded while removals and settings still apply when it is full; after replay, registrations reach the replayed manager, including re-entrant ones during replay. All suites, spotless, detekt and apiCheck pass.

Manual testing

Not needed, not wired in.

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

AR Abdul Azeez and others added 2 commits November 2, 2026 10:55
Deferred User, Notifications, InAppMessages, Session and Location
managers, plus JWT invalidated listeners. Every call re-resolves the real
manager through a ManagerSource. Before init, registrations are queued
(a removal cancels its queued add, a setting keeps its latest value) and
replayed in order once; other calls are logged and dropped.

Not wired in yet; OneSignalImpV2 hands these out later in this stack.

Co-authored-by: Cursor <cursoragent@cursor.com>
…manager

Caps queued listener registrations at 100 per manager; past the cap an add takes
the unqueued path and is logged as ignored. Removals and setting changes are never
capped, so they still cancel or replace queued entries. After replay, registrations
and settings go to the manager already replayed into, so a listener that calls back
in during replay, or a registration after init fails post-replay, is not dropped.
A failing pushSubscription resolve during replay is logged instead of failing init.

Co-authored-by: Cursor <cursoragent@cursor.com>
@github-actions

github-actions Bot commented Nov 2, 2026

Copy link
Copy Markdown
Contributor

📊 Diff Coverage Report

Diff Coverage Report (Changed Lines Only)

Gate: aggregate coverage on changed executable lines must be ≥ 80% (JaCoCo line data for lines touched in the diff).

Changed Files Coverage

  • DeferredManagers.kt: 121/128 touched executable lines (94.5%) (326 touched lines in diff)

Overall (aggregate gate)

121/128 touched executable lines covered (94.5% — requires ≥ 80%)

📥 View workflow run

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.

1 participant