Show update alert when checking from menu bar with no window open - #55
Merged
Merged
Conversation
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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
SPUStandardUserDrivershows its "Checking…" status window and result alerts (up-to-date / errors) as transient panels.WindowObserver.isAppWindowdeliberately does not count Sparkle's panels as app windows, so when the "Checking…" window closes,WindowObserverreverts 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
.regularand activate it for the full duration of a user-initiated update session:DockPolicy.activationPolicygains anupdaterSessionActiveinput (→.regularwhile a session runs), plusupdaterSessionActiveKeyand anupdaterSessionActive(from:)notification-payload parser.UpdaterManager.checkForUpdates()posts.updaterSessionStateChanged(active: true)and callsNSApp.activate(ignoringOtherApps:)inside the click event (the activation must be tied to the user interaction — Sparkle's own later attempt is not enough). A privateUpdaterSessionDelegate(SPUUpdaterDelegate) clears the session inupdater(_:didFinishUpdateCycleFor:error:); Sparkle holds the delegate weakly, so the manager retains it.WindowObserverobserves the notification, tracks the session flag, and re-evaluates the activation policy.Verification
DockPolicyTests🤖 Generated with Claude Code