diff --git a/ApplicationLibrary/Views/Dashboard/Components/StartStopButton.swift b/ApplicationLibrary/Views/Dashboard/Components/StartStopButton.swift index 8434444..8d91170 100644 --- a/ApplicationLibrary/Views/Dashboard/Components/StartStopButton.swift +++ b/ApplicationLibrary/Views/Dashboard/Components/StartStopButton.swift @@ -154,10 +154,8 @@ public struct StartStopButton: View { @available(iOS 16.0, macOS 13.0, tvOS 17.0, *) private func checkStartupError() async { - do { - try await profile.fetchLastDisconnectError() - } catch { - alert = AlertState(title: String(localized: "Service Error"), message: error.localizedDescription) + if let alertState = await profile.checkLastDisconnectError() { + alert = alertState } } diff --git a/ApplicationLibrary/Views/Dashboard/DashboardViewModel.swift b/ApplicationLibrary/Views/Dashboard/DashboardViewModel.swift index 0976b05..400713a 100644 --- a/ApplicationLibrary/Views/Dashboard/DashboardViewModel.swift +++ b/ApplicationLibrary/Views/Dashboard/DashboardViewModel.swift @@ -4,6 +4,10 @@ import Library import NetworkExtension import SwiftUI +#if os(macOS) + import AppKit +#endif + @MainActor public final class DashboardViewModel: BaseViewModel { @Published public var profileList: [ProfilePreview] = [] @@ -154,32 +158,38 @@ public final class DashboardViewModel: BaseViewModel { @available(iOS 16.0, macOS 13.0, tvOS 17.0, *) nonisolated func checkLastDisconnectError(profile: ExtensionProfile) async { + if let alertState = await profile.checkLastDisconnectError() { + await MainActor.run { + alert = alertState + } + } + } +} + +@available(iOS 16.0, macOS 13.0, tvOS 17.0, *) +extension ExtensionProfile { + public nonisolated func checkLastDisconnectError() async -> AlertState? { do { - try await profile.fetchLastDisconnectError() - return + try await fetchLastDisconnectError() + return nil } catch { - let myError = error as NSError + let nsError = error as NSError #if os(macOS) - if myError.domain == "Library.FullDiskAccessPermissionRequired" { - await MainActor.run { - alert = AlertState( - title: String(localized: "Full Disk Access permission is required"), - message: String(localized: "Please grant the permission for **SFMExtension**, then we can continue."), - primaryButton: .default(String(localized: "Authorize"), action: openFDASettings), - secondaryButton: .cancel() - ) - } - return + if nsError.domain == "Library.FullDiskAccessPermissionRequired" { + return AlertState( + title: String(localized: "Full Disk Access permission is required"), + message: String(localized: "Please grant the permission for **SFMExtension**, then we can continue."), + primaryButton: .default(String(localized: "Authorize"), action: Self.openFDASettings), + secondaryButton: .cancel() + ) } #endif - await MainActor.run { - alert = AlertState(title: String(localized: "Service Error"), message: myError.localizedDescription) - } + return AlertState(title: String(localized: "Service Error"), message: nsError.localizedDescription) } } #if os(macOS) - private func openFDASettings() { + private static func openFDASettings() { if NSWorkspace.shared.open(URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_AllFiles")!) { return } diff --git a/SFM.System/Info.plist b/SFM.System/Info.plist index 4902489..bd4fbeb 100644 --- a/SFM.System/Info.plist +++ b/SFM.System/Info.plist @@ -111,5 +111,7 @@ en NSLocalNetworkUsageDescription As a universal proxy platform, sing-box configures routing according to your configuration. + NSCameraUsageDescription + Camera access is required to scan QR codes for importing profiles.