// // DisplayChainedError.swift // MullvadVPN // // Created by pronebird on 04/06/2020. // Copyright © 2020 Mullvad VPN AB. All rights reserved. // import Foundation import StoreKit protocol DisplayChainedError { var errorChainDescription: String? { get } } extension RestError: DisplayChainedError { var errorChainDescription: String? { switch self { case .network(let urlError): return String( format: NSLocalizedString("Network error: %@", comment: ""), urlError.localizedDescription ) case .server(let serverError): if let knownErrorDescription = serverError.errorDescription { return knownErrorDescription } else { return String( format: NSLocalizedString("Server error: %@", comment: ""), serverError.error ?? "(empty)" ) } case .encodePayload: return NSLocalizedString("Server request encoding error", comment: "") case .decodeSuccessResponse: return NSLocalizedString("Server success response decoding error", comment: "") case .decodeErrorResponse: return NSLocalizedString("Server error response decoding error", comment: "") } } } extension TunnelManager.Error: DisplayChainedError { var errorChainDescription: String? { switch self { case .loadAllVPNConfigurations(let systemError): return String(format: NSLocalizedString("Failed to load system VPN configurations: %@", comment: ""), systemError.localizedDescription) case .reloadVPNConfiguration(let systemError): return String(format: NSLocalizedString("Failed to reload a VPN configuration: %@", comment: ""), systemError.localizedDescription) case .saveVPNConfiguration(let systemError): return String(format: NSLocalizedString("Failed to save a VPN tunnel configuration: %@", comment: ""), systemError.localizedDescription) case .obtainPersistentKeychainReference(_): return NSLocalizedString("Failed to obtain the persistent keychain reference for the VPN configuration", comment: "") case .startVPNTunnel(let systemError): return String(format: NSLocalizedString("System error when starting the VPN tunnel: %@", comment: ""), systemError.localizedDescription) case .removeVPNConfiguration(let systemError): return String(format: NSLocalizedString("Failed to remove the system VPN configuration: %@", comment: ""), systemError.localizedDescription) case .removeInconsistentVPNConfiguration(let systemError): return String(format: NSLocalizedString("Failed to remove the outdated system VPN configuration: %@", comment: ""), systemError.localizedDescription) case .readTunnelSettings(_): return NSLocalizedString("Failed to read tunnel settings", comment: "") case .addTunnelSettings(_): return NSLocalizedString("Failed to add tunnel settings", comment: "") case .updateTunnelSettings(_): return NSLocalizedString("Failed to update tunnel settings", comment: "") case .removeTunnelSettings(_): return NSLocalizedString("Failed to remove tunnel settings", comment: "") case .migrateTunnelSettings(_): return NSLocalizedString("Failed to migrate tunnel settings", comment: "") case .pushWireguardKey(let restError): let reason = restError.errorChainDescription ?? "" var message = String(format: NSLocalizedString("Failed to send the WireGuard key to server: %@", comment: ""), reason) if case .server(.keyLimitReached) = restError { message.append("\n\n") message.append(NSLocalizedString("Remove unused WireGuard keys and try again", comment: "")) } return message case .replaceWireguardKey(let restError): let reason = restError.errorChainDescription ?? "" var message = String(format: NSLocalizedString("Failed to replace the WireGuard key on server: %@", comment: ""), reason) if case .server(.keyLimitReached) = restError { message.append("\n\n") message.append(NSLocalizedString("Remove unused WireGuard keys and try again", comment: "")) } return message case .removeWireguardKey: // This error is never displayed anywhere return nil case .verifyWireguardKey(let restError): let reason = restError.errorChainDescription ?? "" return String(format: NSLocalizedString("Failed to verify the WireGuard key on server: %@", comment: ""), reason) case .missingAccount: return NSLocalizedString("Internal error: missing account", comment: "") } } } extension Account.Error: DisplayChainedError { var errorChainDescription: String? { switch self { case .createAccount(let restError), .verifyAccount(let restError): return restError.errorChainDescription case .tunnelConfiguration(let tunnelError): return tunnelError.errorChainDescription } } } extension SKError: LocalizedError { public var errorDescription: String? { switch self.code { case .unknown: return NSLocalizedString("Unknown error", comment: "") case .clientInvalid: return NSLocalizedString("Client is not allowed to issue the request", comment: "") case .paymentCancelled: return NSLocalizedString("User cancelled the request", comment: "") case .paymentInvalid: return NSLocalizedString("Invalid purchase identifier", comment: "") case .paymentNotAllowed: return NSLocalizedString("This device is not allowed to make the payment", comment: "") default: return self.localizedDescription } } } extension AppStorePaymentManager.Error: DisplayChainedError { var errorChainDescription: String? { switch self { case .noAccountSet: return NSLocalizedString("Internal error: account is not set", comment: "") case .readReceipt(let readReceiptError): switch readReceiptError { case .refresh(let storeError): let skErrorMessage = (storeError as? SKError)?.errorDescription ?? storeError.localizedDescription return String(format: NSLocalizedString("Cannot refresh the AppStore receipt: %@", comment: ""), skErrorMessage) case .io(let ioError): return String(format: NSLocalizedString("Cannot read the AppStore receipt from disk: %@", comment: ""), ioError.localizedDescription) case .doesNotExist: return NSLocalizedString("AppStore receipt is not found on disk.", comment: "") } case .sendReceipt(let restError): let reason = restError.errorChainDescription ?? "" let format = NSLocalizedString(#""" Failed to send the receipt to server: %@ Please retry by using the "Restore purchases" button. """#, comment: "") return String(format: format, reason) case .storePayment(let storeError): return (storeError as? SKError)?.errorDescription ?? storeError.localizedDescription } } }