From 963cc8ede0a9e1a903200650cf86b8bee3cb4a6a Mon Sep 17 00:00:00 2001 From: Mariusz Klochowicz Date: Wed, 10 Sep 2025 11:24:58 +0930 Subject: [PATCH] fix(apple): Enforce single Firezone instance (#10313) show an alert to the user and ask to quit previous Firezone instance manually before starting a new one. Resolves: #10295 --------- Signed-off-by: Mariusz Klochowicz Co-authored-by: Jamil --- .../Firezone/Application/FirezoneApp.swift | 37 +++++++++++++++++++ website/src/components/Changelog/Apple.tsx | 4 ++ 2 files changed, 41 insertions(+) diff --git a/swift/apple/Firezone/Application/FirezoneApp.swift b/swift/apple/Firezone/Application/FirezoneApp.swift index c4e577632..fe453d05b 100644 --- a/swift/apple/Firezone/Application/FirezoneApp.swift +++ b/swift/apple/Firezone/Application/FirezoneApp.swift @@ -73,6 +73,11 @@ struct FirezoneApp: App { var menuBar: MenuBar? var store: Store? + func applicationWillFinishLaunching(_ notification: Notification) { + // Enforce single instance BEFORE the app fully launches + enforceSingleInstance() + } + func applicationDidFinishLaunching(_: Notification) { if let store { menuBar = MenuBar(store: store) @@ -86,6 +91,38 @@ struct FirezoneApp: App { maybeShowOutdatedAlert() } + private func enforceSingleInstance() { + // Get the actual bundle identifier from the running app + guard let bundleId = Bundle.main.bundleIdentifier else { return } + + let runningApps = NSRunningApplication.runningApplications( + withBundleIdentifier: bundleId + ) + + guard runningApps.count > 1 else { return } + + for app in runningApps where app != NSRunningApplication.current { + DispatchQueue.main.async { + let alert = NSAlert() + alert.messageText = "Another Firezone Instance Detected" + alert.informativeText = """ + Another instance of Firezone is already running. \ + Please quit the other instance from the menu bar to continue. + + Location: \(app.bundleURL?.path ?? "Unknown") + """ + alert.alertStyle = .warning + alert.addButton(withTitle: "OK") + + // Show alert + alert.runModal() + + // Exit this instance since we can't terminate the other one + NSApp.terminate(nil) + } + } + } + private func maybeShowOutdatedAlert() { let osVersion = ProcessInfo.processInfo.operatingSystemVersion diff --git a/website/src/components/Changelog/Apple.tsx b/website/src/components/Changelog/Apple.tsx index 854b84988..980c927f0 100644 --- a/website/src/components/Changelog/Apple.tsx +++ b/website/src/components/Changelog/Apple.tsx @@ -25,6 +25,10 @@ export default function Apple() { {/* When you cut a release, remove any solved issues from the "known issues" lists over in `client-apps`. This must not be done when the issue's PR merges. */} + + Fixes an issue where multiple concurrent Firezone macOS clients could + run simultaneously. We now enforce a single instance of the client. + Fixes a minor DNS cache bug where newly-added DNS resources may not resolve for a few seconds after showing up in the Resource List.