From 47befe37f410ee5e582b4eddb171ac3627b6d308 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 19 Jun 2025 00:44:03 +0200 Subject: [PATCH] fix(apple): disable false-positive "App hang" reports (#9568) As recommended by the Sentry team [0], "App hang" tracking should be disabled before calling into certain system APIs like showing alerts to prevent false-positives. [0]: https://github.com/getsentry/sentry-cocoa/issues/3472 --- swift/apple/Firezone/Application/FirezoneApp.swift | 3 +++ .../Sources/FirezoneKit/Models/Configuration.swift | 10 ++++++++-- .../FirezoneKit/Models/SessionNotification.swift | 4 ++++ .../Sources/FirezoneKit/Views/GrantVPNView.swift | 3 +++ .../Sources/FirezoneKit/Views/MenuBar.swift | 3 +++ .../Sources/FirezoneKit/Views/macOSAlert.swift | 4 ++++ 6 files changed, 25 insertions(+), 2 deletions(-) diff --git a/swift/apple/Firezone/Application/FirezoneApp.swift b/swift/apple/Firezone/Application/FirezoneApp.swift index 8d1c8a6bc..e17b0bec0 100644 --- a/swift/apple/Firezone/Application/FirezoneApp.swift +++ b/swift/apple/Firezone/Application/FirezoneApp.swift @@ -6,6 +6,7 @@ import FirezoneKit import SwiftUI +import Sentry @main struct FirezoneApp: App { @@ -105,6 +106,8 @@ struct FirezoneApp: App { alert.addButton(withTitle: "Open System Preferences") alert.addButton(withTitle: "OK") + SentrySDK.pauseAppHangTracking() + defer { SentrySDK.resumeAppHangTracking() } let response = alert.runModal() if response == .alertFirstButtonReturn { diff --git a/swift/apple/FirezoneKit/Sources/FirezoneKit/Models/Configuration.swift b/swift/apple/FirezoneKit/Sources/FirezoneKit/Models/Configuration.swift index 1370954fd..949b77e29 100644 --- a/swift/apple/FirezoneKit/Sources/FirezoneKit/Models/Configuration.swift +++ b/swift/apple/FirezoneKit/Sources/FirezoneKit/Models/Configuration.swift @@ -6,6 +6,7 @@ // A thin wrapper around UserDefaults for user and admin managed app configuration. import Foundation +import Sentry #if os(macOS) import ServiceManagement @@ -142,12 +143,17 @@ public class Configuration: ObservableObject { // so this feature only enabled for macOS 13 and higher given the tiny Firezone installbase for macOS 12. func updateAppService() async throws { if #available(macOS 13.0, *) { - if !startOnLogin, SMAppService.mainApp.status == .enabled { + // Getting the status initially appears to be blocking sometimes + SentrySDK.pauseAppHangTracking() + defer { SentrySDK.resumeAppHangTracking() } + let status = SMAppService.mainApp.status + + if !startOnLogin, status == .enabled { try await SMAppService.mainApp.unregister() return } - if startOnLogin, SMAppService.mainApp.status != .enabled { + if startOnLogin, status != .enabled { try SMAppService.mainApp.register() } } diff --git a/swift/apple/FirezoneKit/Sources/FirezoneKit/Models/SessionNotification.swift b/swift/apple/FirezoneKit/Sources/FirezoneKit/Models/SessionNotification.swift index 7bf52b742..0851911f3 100644 --- a/swift/apple/FirezoneKit/Sources/FirezoneKit/Models/SessionNotification.swift +++ b/swift/apple/FirezoneKit/Sources/FirezoneKit/Models/SessionNotification.swift @@ -6,6 +6,7 @@ import Foundation import UserNotifications +import Sentry #if os(macOS) import AppKit @@ -106,7 +107,10 @@ public class SessionNotification: NSObject { NSApp.activate(ignoringOtherApps: true) await withCheckedContinuation { continuation in + SentrySDK.pauseAppHangTracking() + defer { SentrySDK.resumeAppHangTracking() } let response = alert.runModal() + if response == NSApplication.ModalResponse.alertFirstButtonReturn { Log.log("\(#function): 'Sign In' clicked in notification") signInHandler() diff --git a/swift/apple/FirezoneKit/Sources/FirezoneKit/Views/GrantVPNView.swift b/swift/apple/FirezoneKit/Sources/FirezoneKit/Views/GrantVPNView.swift index 198c26ec9..a8bdbc7ce 100644 --- a/swift/apple/FirezoneKit/Sources/FirezoneKit/Views/GrantVPNView.swift +++ b/swift/apple/FirezoneKit/Sources/FirezoneKit/Views/GrantVPNView.swift @@ -7,6 +7,7 @@ import Combine import SwiftUI +import Sentry #if os(macOS) import SystemExtensions @@ -167,6 +168,8 @@ struct GrantVPNView: View { alert.messageText = "Permission required." alert.informativeText = "Firezone requires permission to install VPN configurations. Without it, all functionality will be disabled." + SentrySDK.pauseAppHangTracking() + defer { SentrySDK.resumeAppHangTracking() } _ = alert.runModal() } else { throw error diff --git a/swift/apple/FirezoneKit/Sources/FirezoneKit/Views/MenuBar.swift b/swift/apple/FirezoneKit/Sources/FirezoneKit/Views/MenuBar.swift index ada98057d..e85529031 100644 --- a/swift/apple/FirezoneKit/Sources/FirezoneKit/Views/MenuBar.swift +++ b/swift/apple/FirezoneKit/Sources/FirezoneKit/Views/MenuBar.swift @@ -12,6 +12,7 @@ import Foundation import NetworkExtension import OSLog import SwiftUI +import Sentry #if os(macOS) @MainActor @@ -748,6 +749,8 @@ import SwiftUI let alert = NSAlert() alert.messageText = "Firezone requires permission to install VPN configurations. Without it, all functionality will be disabled." + SentrySDK.pauseAppHangTracking() + defer { SentrySDK.resumeAppHangTracking() } _ = alert.runModal() } else { throw error diff --git a/swift/apple/FirezoneKit/Sources/FirezoneKit/Views/macOSAlert.swift b/swift/apple/FirezoneKit/Sources/FirezoneKit/Views/macOSAlert.swift index 2604a0dd4..2b6442848 100644 --- a/swift/apple/FirezoneKit/Sources/FirezoneKit/Views/macOSAlert.swift +++ b/swift/apple/FirezoneKit/Sources/FirezoneKit/Views/macOSAlert.swift @@ -9,6 +9,7 @@ import SystemExtensions import AppKit import NetworkExtension + import Sentry protocol UserFriendlyError { func userMessage() -> String? @@ -205,6 +206,9 @@ alert.messageText = "An error occurred." alert.informativeText = message alert.alertStyle = .critical + + SentrySDK.pauseAppHangTracking() + defer { SentrySDK.resumeAppHangTracking() } alert.runModal() } }