Hive
bug: macOS menu bar app shows two status items; the older one is stuck on an endless spinner
GitHub issue · Open
What happened?
Every time I launch the Tuist macOS menu bar app, two Tuist icons appear in the menu bar. The left one works normally. The right one is dead weight: clicking it opens a panel with a spinner (ProgressView) that never resolves. Quitting and reopening the app reproduces it every time — always two icons.
Both icons belong to the same single Tuist process (verified with ps — one PID, no login item, single app bundle), so this is not a duplicate-install problem.
Expected: one menu bar icon.
Root cause analysis
In app/Sources/TuistApp/TuistApp.swift, the FluidMenuBarExtra is created inside the SwiftUI Scene body:
var body: some Scene {
appDelegate.menuBarExtra = FluidMenuBarExtra(title: "Tuist", image: "MenuBarIcon") {
if bootstrapper.isReady {
... MenuBarView(...) ...
} else {
ProgressView()
}
}
return Settings { EmptyView() }
...
}
Three things combine here:
FluidMenuBarExtra.initcreates anNSStatusItemimmediately, and its docs say it should be “initialized once during the lifecycle of your app”. Creating it inbodyviolates that, becausebodyis re-evaluated.bodyis re-evaluated shortly after launch:AppBootstrapper.initruns an async task that flips the@Published isReady, so:- Evaluation #1 (at launch,
isReady == false) →FluidMenuBarExtra#1. - Evaluation #2 (bootstrap done,
isReady == true) →FluidMenuBarExtra#2. The first instance isn’t reliably torn down, so its status item stays in the menu bar. (New status items are inserted to the left, which is why the right icon is always the broken one.)
- Evaluation #1 (at launch,
- The stuck spinner is explained by
FluidMenuBarExtraWindow: it evaluates thecontentclosure once at init (setContentSize(hostingView.intrinsicContentSize)forces the lazyNSHostingView). Theif bootstrapper.isReadybranch is taken outside of any observingView.body, so instance #1’s window is a frozen snapshot of theProgressViewbranch and never re-renders whenisReadyflips.
A fix would be to create the FluidMenuBarExtra only once, and move the isReady branch into a small wrapper View that holds the bootstrapper as @ObservedObject, so the single window transitions from spinner → MenuBarView reactively. Happy to send a PR for this.
How do we reproduce it?
- Install the Tuist macOS app 0.25.7 and launch it.
- Watch the menu bar: two Tuist icons appear (the second appears almost immediately after the first, when bootstrap completes).
- Click the right icon → panel with a spinner that never finishes. Click the left icon → normal menu.
Error log
No error is logged. The app's session log shows a normal startup
(authentication state initialized, token valid); the duplicate
status item produces no log output.
macOS version
15.6.1
Tuist version
Tuist.app 0.25.7
Xcode version
26.0.1
Investigate, reproduce, or fix this item in an isolated repository. Each Flight preserves its outcome and agent session.
Start a Flight and preserve its objective, outcome, and session here.
No GitHub comments yet.