From f0c5ecd84f1e5ed66b3acae85eb08ac949545192 Mon Sep 17 00:00:00 2001 From: Davide De Rosa Date: Fri, 20 May 2022 08:29:14 +0200 Subject: [PATCH] Handle configuration errors out of VPN status On configuration error, retain information about the profile that triggered the error. For now, present an alert, but with this information the UI can be easily changed later. --- Passepartout/App/L10n/Core+L10n.swift | 9 +++++++++ Passepartout/App/Views/OrganizerView.swift | 13 +++++++++---- .../Managers/VPNManager+Configuration.swift | 2 +- .../PassepartoutCore/Managers/VPNManager.swift | 5 ++++- 4 files changed, 23 insertions(+), 6 deletions(-) diff --git a/Passepartout/App/L10n/Core+L10n.swift b/Passepartout/App/L10n/Core+L10n.swift index aa925740..15b4db10 100644 --- a/Passepartout/App/L10n/Core+L10n.swift +++ b/Passepartout/App/L10n/Core+L10n.swift @@ -26,6 +26,15 @@ import Foundation import PassepartoutCore +extension Error { + var localizedAppDescription: String { + if let errorDescription = (self as? PassepartoutError)?.localizedAppDescription, !errorDescription.isEmpty { + return "\(errorDescription)." + } + return localizedDescription + } +} + extension PassepartoutError { var localizedAppDescription: String? { let V = L10n.Global.Errors.self diff --git a/Passepartout/App/Views/OrganizerView.swift b/Passepartout/App/Views/OrganizerView.swift index 707d9012..23be8485 100644 --- a/Passepartout/App/Views/OrganizerView.swift +++ b/Passepartout/App/Views/OrganizerView.swift @@ -30,7 +30,7 @@ struct OrganizerView: View { enum AlertType: Identifiable { case subscribeReddit - case error(String, Error) + case error(String, String) // XXX: alert ids var id: Int { @@ -79,6 +79,11 @@ struct OrganizerView: View { onCompletion: onHostFileImporterResult ).onOpenURL(perform: onOpenURL) .themePrimaryView() + + // VPN configuration error publisher (no need to observe VPNManager) + .onReceive(VPNManager.shared.configurationError) { + alertType = .error($0.profile.header.name, $0.error.localizedAppDescription) + } } private var hiddenSceneView: some View { @@ -105,7 +110,7 @@ extension OrganizerView { case .failure(let error): alertType = .error( L10n.Menu.Contextual.AddProfile.fromFiles, - error + error.localizedDescription ) } } @@ -129,10 +134,10 @@ extension OrganizerView { } ) - case .error(let title, let error): + case .error(let title, let errorDescription): return Alert( title: Text(title), - message: Text(error.localizedDescription), + message: Text(errorDescription), dismissButton: .cancel(Text(L10n.Global.Strings.ok)) ) } diff --git a/PassepartoutCore/Sources/PassepartoutCore/Managers/VPNManager+Configuration.swift b/PassepartoutCore/Sources/PassepartoutCore/Managers/VPNManager+Configuration.swift index d0c7a309..1fe6196e 100644 --- a/PassepartoutCore/Sources/PassepartoutCore/Managers/VPNManager+Configuration.swift +++ b/PassepartoutCore/Sources/PassepartoutCore/Managers/VPNManager+Configuration.swift @@ -87,7 +87,7 @@ extension VPNManager { pp_log.error("Unable to build VPNConfiguration: \(error)") // UI is certainly interested in configuration errors - lastError = error + configurationError.send((profile, error)) throw error } diff --git a/PassepartoutCore/Sources/PassepartoutCore/Managers/VPNManager.swift b/PassepartoutCore/Sources/PassepartoutCore/Managers/VPNManager.swift index 012135e1..52599fa0 100644 --- a/PassepartoutCore/Sources/PassepartoutCore/Managers/VPNManager.swift +++ b/PassepartoutCore/Sources/PassepartoutCore/Managers/VPNManager.swift @@ -30,6 +30,7 @@ import TunnelKitOpenVPNManager @MainActor public class VPNManager: ObservableObject { + public typealias ConfigurationError = (profile: Profile, error: Error) // MARK: Initialization @@ -49,7 +50,7 @@ public class VPNManager: ObservableObject { public let currentState: ObservableState - public internal(set) var lastError: Error? { + public private(set) var lastError: Error? { get { currentState.lastError } @@ -58,6 +59,8 @@ public class VPNManager: ObservableObject { } } + public let configurationError = PassthroughSubject() + // MARK: Internals private var lastProfile: Profile = .placeholder