From 7390d68a335ab96e42c217df128394aba3fc9c5a Mon Sep 17 00:00:00 2001 From: maxsoch Date: Mon, 6 Jul 2026 21:26:14 +0200 Subject: [PATCH] fix: deliver runBrew completion on the main thread MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Completions were invoked on a background queue. Most call sites wrapped their UI work in DispatchQueue.main.async, but upgradeSingle's completion calls refreshAll(), which sets statusMenuItem.title directly — an AppKit mutation off the main thread. It also raced on cachedOutdated / lastOutdatedFetch (written from background, read from main). runBrew now always dispatches its completion to the main queue, so every caller can safely touch UI, and the now-redundant inner main.async hops are removed. Co-Authored-By: Claude Fable 5 --- BrewBar/BrewBarApp.swift | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/BrewBar/BrewBarApp.swift b/BrewBar/BrewBarApp.swift index bbc2211..44b7ecf 100644 --- a/BrewBar/BrewBarApp.swift +++ b/BrewBar/BrewBarApp.swift @@ -194,7 +194,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { try process.run() } catch { self.log("ERROR: \(error)\n") - completion("") + DispatchQueue.main.async { completion("") } return } @@ -204,7 +204,8 @@ class AppDelegate: NSObject, NSApplicationDelegate { let output = String(data: data, encoding: .utf8) ?? "" self.log(output) - completion(output) + // completion always on main: callers update AppKit UI, which is main-thread only + DispatchQueue.main.async { completion(output) } } } @@ -249,14 +250,12 @@ class AppDelegate: NSObject, NSApplicationDelegate { self.cachedOutdated = lines self.lastOutdatedFetch = Date() - DispatchQueue.main.async { - self.updateOutdatedMenu(with: lines) - self.updateStatus(count: lines.count) - if lines.isEmpty { - self.setMenuBarIcon("brewbar-uptodate") - } else { - self.setMenuBarIcon("brewbar-outdated") - } + self.updateOutdatedMenu(with: lines) + self.updateStatus(count: lines.count) + if lines.isEmpty { + self.setMenuBarIcon("brewbar-uptodate") + } else { + self.setMenuBarIcon("brewbar-outdated") } } } @@ -305,10 +304,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { func refreshBrewVersion() { runBrew("--version") { output in let firstLine = BrewParser.parseVersion(output) - - DispatchQueue.main.async { - self.versionItem.title = "🏷 \(firstLine)" - } + self.versionItem.title = "🏷 \(firstLine)" } }