Skip to content

Show update alert when checking from menu bar with no window open - #55

Merged
sapsaldog merged 1 commit into
mainfrom
fix/updater-alert-when-no-window
Jun 10, 2026
Merged

sapsaldog merged 1 commit into
mainfrom
fix/updater-alert-when-no-window

Conversation

@sapsaldog

Copy link
Copy Markdown
Owner

Problem

When the app runs as a menu-bar accessory with no window open, clicking Check for Updates did nothing visible — no "Checking…" window, no "You're up to date" alert, no error.

Root cause

Sparkle's SPUStandardUserDriver shows its "Checking…" status window and result alerts (up-to-date / errors) as transient panels. WindowObserver.isAppWindow deliberately does not count Sparkle's panels as app windows, so when the "Checking…" window closes, WindowObserver reverts the activation policy to .accessory. That deactivates the app, and on macOS 14+ cooperative activation (NSApp.activate()) will not refocus an app without a recent user interaction — so the result alert opens behind other apps' windows, invisible.

Fix

Hold the app in .regular and activate it for the full duration of a user-initiated update session:

  • DockPolicy.activationPolicy gains an updaterSessionActive input (→ .regular while a session runs), plus updaterSessionActiveKey and an updaterSessionActive(from:) notification-payload parser.
  • UpdaterManager.checkForUpdates() posts .updaterSessionStateChanged(active: true) and calls NSApp.activate(ignoringOtherApps:) inside the click event (the activation must be tied to the user interaction — Sparkle's own later attempt is not enough). A private UpdaterSessionDelegate (SPUUpdaterDelegate) clears the session in updater(_:didFinishUpdateCycleFor:error:); Sparkle holds the delegate weakly, so the manager retains it.
  • WindowObserver observes the notification, tracks the session flag, and re-evaluates the activation policy.

Verification

  • Live: with no window open, Check for Updates now brings the app to front and shows the "You're up to date!" alert on top.
  • ✅ Full unit suite (481 tests) passes
  • ✅ SwiftLint clean
  • ✅ 100% coverage gate passes — the new arg + parser are covered by DockPolicyTests

🤖 Generated with Claude Code

When the app runs as a menu-bar accessory with no window open, clicking
"Check for Updates" did nothing visible. Sparkle's "Checking…" window and
result alerts ("You're up to date", errors) are transient panels that
WindowObserver deliberately does not count as app windows. So when the
"Checking…" window closed, WindowObserver reverted the activation policy to
.accessory, deactivating the app — and on macOS 14+ cooperative activation
will not refocus the app without a recent user interaction, leaving the
alert hidden behind other apps' windows.

Hold the app in .regular and activate it for the duration of a user-initiated
update session:

- DockPolicy.activationPolicy gains an `updaterSessionActive` input (→ .regular
  while a session runs), plus `updaterSessionActiveKey` and an
  `updaterSessionActive(from:)` notification-payload parser.
- UpdaterManager.checkForUpdates() posts .updaterSessionStateChanged(active:true)
  and calls NSApp.activate(ignoringOtherApps:) inside the click event (the
  activation must be tied to the user interaction). A private
  UpdaterSessionDelegate (SPUUpdaterDelegate) clears the session in
  updater(_:didFinishUpdateCycleFor:error:); Sparkle holds it weakly so the
  manager retains it.
- WindowObserver observes the notification, tracks the session flag, and
  re-evaluates the activation policy.

Verified live: the "You're up to date" alert now appears in front with the app
active. Full unit suite (481 tests) passes, SwiftLint clean, 100% coverage gate
passes (new arg + parser covered by DockPolicyTests).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@sapsaldog
sapsaldog merged commit 0725bae into main Jun 10, 2026
2 checks passed
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