diff --git a/Configuration/BuildNumber.xcconfig b/Configuration/BuildNumber.xcconfig index ca1afd458a..e94261020d 100644 --- a/Configuration/BuildNumber.xcconfig +++ b/Configuration/BuildNumber.xcconfig @@ -1 +1 @@ -CURRENT_PROJECT_VERSION = 180 +CURRENT_PROJECT_VERSION = 181 diff --git a/DuckDuckGo/NetworkProtection/AppAndExtensionAndAgentTargets/NetworkProtectionPixelEvent.swift b/DuckDuckGo/NetworkProtection/AppAndExtensionAndAgentTargets/NetworkProtectionPixelEvent.swift index 50a2b96444..e9b9f4c847 100644 --- a/DuckDuckGo/NetworkProtection/AppAndExtensionAndAgentTargets/NetworkProtectionPixelEvent.swift +++ b/DuckDuckGo/NetworkProtection/AppAndExtensionAndAgentTargets/NetworkProtectionPixelEvent.swift @@ -28,6 +28,7 @@ enum NetworkProtectionPixelEvent: PixelKitEventV2 { case networkProtectionControllerStartAttempt case networkProtectionControllerStartSuccess + case networkProtectionControllerStartCancelled case networkProtectionControllerStartFailure(_ error: Error) case networkProtectionTunnelStartAttempt @@ -122,6 +123,9 @@ enum NetworkProtectionPixelEvent: PixelKitEventV2 { case .networkProtectionControllerStartSuccess: return "netp_controller_start_success" + case .networkProtectionControllerStartCancelled: + return "netp_controller_start_cancelled" + case .networkProtectionControllerStartFailure: return "netp_controller_start_failure" @@ -344,6 +348,7 @@ enum NetworkProtectionPixelEvent: PixelKitEventV2 { .networkProtectionNewUser, .networkProtectionControllerStartAttempt, .networkProtectionControllerStartSuccess, + .networkProtectionControllerStartCancelled, .networkProtectionControllerStartFailure, .networkProtectionTunnelStartAttempt, .networkProtectionTunnelStartSuccess, @@ -415,6 +420,7 @@ enum NetworkProtectionPixelEvent: PixelKitEventV2 { .networkProtectionNewUser, .networkProtectionControllerStartAttempt, .networkProtectionControllerStartSuccess, + .networkProtectionControllerStartCancelled, .networkProtectionTunnelStartAttempt, .networkProtectionTunnelStartSuccess, .networkProtectionTunnelStopAttempt, diff --git a/DuckDuckGo/NetworkProtection/AppTargets/BothAppTargets/NetworkProtectionTunnelController.swift b/DuckDuckGo/NetworkProtection/AppTargets/BothAppTargets/NetworkProtectionTunnelController.swift index b1bebcadbb..ed20ffef1d 100644 --- a/DuckDuckGo/NetworkProtection/AppTargets/BothAppTargets/NetworkProtectionTunnelController.swift +++ b/DuckDuckGo/NetworkProtection/AppTargets/BothAppTargets/NetworkProtectionTunnelController.swift @@ -448,14 +448,18 @@ final class NetworkProtectionTunnelController: TunnelController, TunnelSessionPr // MARK: - Starting & Stopping the VPN - enum StartError: LocalizedError { + enum StartError: LocalizedError, CustomNSError { + case cancelled case noAuthToken case connectionStatusInvalid case connectionAlreadyStarted case simulateControllerFailureError + case startTunnelFailure(_ error: Error) var errorDescription: String? { switch self { + case .cancelled: + return nil case .noAuthToken: return "You need a subscription to start the VPN" case .connectionAlreadyStarted: @@ -473,6 +477,34 @@ final class NetworkProtectionTunnelController: TunnelController, TunnelSessionPr #endif case .simulateControllerFailureError: return "Simulated a controller error as requested" + case .startTunnelFailure(let error): + return error.localizedDescription + } + } + + var errorCode: Int { + switch self { + case .cancelled: return 0 + // MARK: Setup errors + case .noAuthToken: return 1 + case .connectionStatusInvalid: return 2 + case .connectionAlreadyStarted: return 3 + case .simulateControllerFailureError: return 4 + // MARK: Actual connection attempt issues + case .startTunnelFailure: return 100 + } + } + + var errorUserInfo: [String: Any] { + switch self { + case .cancelled, + .noAuthToken, + .connectionStatusInvalid, + .connectionAlreadyStarted, + .simulateControllerFailureError: + return [:] + case .startTunnelFailure(let error): + return [NSUnderlyingErrorKey: error] } } } @@ -502,6 +534,8 @@ final class NetworkProtectionTunnelController: TunnelController, TunnelSessionPr } catch { if case NEVPNError.configurationReadWriteFailed = error { onboardingStatusRawValue = OnboardingStatus.isOnboarding(step: .userNeedsToAllowVPNConfiguration).rawValue + + throw StartError.cancelled } throw error @@ -528,11 +562,19 @@ final class NetworkProtectionTunnelController: TunnelController, TunnelSessionPr } catch { VPNOperationErrorRecorder().recordControllerStartFailure(error) - PixelKit.fire( - NetworkProtectionPixelEvent.networkProtectionControllerStartFailure(error), frequency: .dailyAndCount, includeAppVersionParameter: true - ) + if case StartError.cancelled = error { + PixelKit.fire( + NetworkProtectionPixelEvent.networkProtectionControllerStartCancelled, frequency: .dailyAndCount, includeAppVersionParameter: true + ) + } else { + PixelKit.fire( + NetworkProtectionPixelEvent.networkProtectionControllerStartFailure(error), frequency: .dailyAndCount, includeAppVersionParameter: true + ) + } - await stop() + if await isConnected { + await stop() + } // Always keep the first error message shown, as it's the more actionable one. if controllerErrorStore.lastErrorMessage == nil { @@ -577,7 +619,11 @@ final class NetworkProtectionTunnelController: TunnelController, TunnelSessionPr throw StartError.simulateControllerFailureError } - try tunnelManager.connection.startVPNTunnel(options: options) + do { + try tunnelManager.connection.startVPNTunnel(options: options) + } catch { + throw StartError.startTunnelFailure(error) + } PixelKit.fire( NetworkProtectionPixelEvent.networkProtectionNewUser,