Skip to content

Suppress silent LA notifications and prevent stale foreground-restart#638

Open
bjorkert wants to merge 1 commit intodevfrom
fix-willpresent-respect-interruption-level
Open

Suppress silent LA notifications and prevent stale foreground-restart#638
bjorkert wants to merge 1 commit intodevfrom
fix-willpresent-respect-interruption-level

Conversation

@bjorkert
Copy link
Copy Markdown
Member

@bjorkert bjorkert commented May 6, 2026

Summary

Three independent fixes for Live Activity behaviour that surfaced together
in a 6.1.0 report (audible notification on LA renewal + a gratuitous LA
restart 37 minutes after a successful background renewal).

  • willPresent no longer forces banner+sound for every notification.
    The handler in AppDelegate returned [.banner, .sound, .badge]
    unconditionally, which meant any notification iOS routed through
    UNUserNotificationCenter while LF was foregrounded would produce
    sound — including the intentionally-silent push-to-start payload
    (interruption-level: passive, empty title/body). The handler now
    returns [] for passive notifications and for ones with empty
    title/body. The four intentional alerts (scheduleRenewalFailed…,
    scheduleApnsCredentialsMissing…, schedulePushToStartTokenMissing…,
    alarms) all use non-empty title/body and the default .active
    interruption level, so they continue to surface normally.

  • pendingForegroundRestart is cleared on every push-to-start
    adoption
    (adoptPushToStartActivity). Without this, a brief
    foreground entry while the renewal overlay was up could latch the
    intent, the user could background again before didBecomeActive ran,
    the background renewal could replace the LA, and the next foreground
    entry — minutes later — would tear down the freshly-renewed LA and
    start another.

  • performForegroundRestart re-checks the latch conditions. Belt
    and braces with the previous fix: even if a future code path latches
    the intent without going through adoption, the deferred restart will
    bail out when renewalFailed/overlayShowing/pushToStartLooksStuck
    no longer hold.

  • Deferred-foreground-restart push-to-start is tagged
    reason="deferred-foreground-restart"
    instead of inheriting the
    "user-start" label from startIfNeeded. The previous tag made the
    stale-latch event indistinguishable from a real user action in logs.
    Threaded through a single-shot nextStartReasonOverride so the
    override is consumed exactly once and startIfNeeded's public surface
    is unchanged.

  • willPresent log line expanded with interruptionLevel and
    whether title/body are empty, so future reports can confirm whether
    iOS is routing the LA push-to-start payload through willPresent.

Three independent fixes for Live Activity behaviour exposed by a 6.1.0
report of audible notifications + a gratuitous restart:

1. AppDelegate's UNUserNotificationCenterDelegate willPresent handler
   returned [.banner, .sound, .badge] for every notification. The Live
   Activity push-to-start payload is intentionally silent (interruption-
   level: passive, empty title/body), so anything iOS routes through
   willPresent while the app is foregrounded would still produce sound.
   Now bails out for passive notifications and for ones with empty
   title/body. Intentional alerts (renewal-failed, APNs missing, push-
   to-start token missing, alarms) all carry non-empty title/body and
   default .active interruption level, so they continue to surface.

2. pendingForegroundRestart could survive a background renewal: a brief
   foreground entry while the renewal overlay was up latched the intent,
   the user backgrounded before didBecomeActive ran, the background
   renewal then replaced the LA, and the next foreground entry minutes
   later fired the deferred restart against a freshly-renewed LA.
   adoptPushToStartActivity now clears the latch on every adoption, and
   performForegroundRestart re-checks the conditions that triggered the
   latch and bails if they no longer hold.

3. The deferred-foreground-restart path went through startIfNeeded and
   was logged as reason="user-start", which made the gratuitous restart
   look like a user action. Threaded a single-shot reason override so
   the deferred path tags its push-to-start as
   reason="deferred-foreground-restart".

Also expanded the willPresent log line with interruption level and
title/body presence so future reports can confirm whether iOS is
routing LA push-to-start payloads through willPresent.
@bjorkert bjorkert requested a review from marionbarker May 6, 2026 13:48
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