diff options
| author | Andrej Mihajlov <and@mullvad.net> | 2022-02-22 11:24:29 +0100 |
|---|---|---|
| committer | Andrej Mihajlov <and@mullvad.net> | 2022-02-22 11:24:29 +0100 |
| commit | 7aea025585bf95654bdd216c48edd3769057aba0 (patch) | |
| tree | 62a3d6630bc24bee9fcac9fac52c8cc31592651e | |
| parent | 1f39933d4881569fc8fb62fdb8ca322ee6af5593 (diff) | |
| parent | dcb8a8a29734dce970025c122c3402640fd215ae (diff) | |
| download | mullvadvpn-7aea025585bf95654bdd216c48edd3769057aba0.tar.xz mullvadvpn-7aea025585bf95654bdd216c48edd3769057aba0.zip | |
Merge branch 'improve-error-output'
| -rw-r--r-- | ios/MullvadVPN.xcodeproj/project.pbxproj | 6 | ||||
| -rw-r--r-- | ios/MullvadVPN/Account.swift | 11 | ||||
| -rw-r--r-- | ios/MullvadVPN/AppStoreReceipt.swift | 6 | ||||
| -rw-r--r-- | ios/MullvadVPN/ChainedError.swift | 69 | ||||
| -rw-r--r-- | ios/MullvadVPN/CodingErrors+ChainedError.swift | 53 | ||||
| -rw-r--r-- | ios/MullvadVPN/ConsolidatedApplicationLog.swift | 4 | ||||
| -rw-r--r-- | ios/MullvadVPN/Logging/LogRotation.swift | 4 | ||||
| -rw-r--r-- | ios/MullvadVPN/REST/RESTError.swift | 10 | ||||
| -rw-r--r-- | ios/MullvadVPN/RelayCache/RelayCacheError.swift | 16 | ||||
| -rw-r--r-- | ios/MullvadVPN/TunnelIPC/TunnelIPCError.swift | 8 | ||||
| -rw-r--r-- | ios/MullvadVPN/TunnelManager/TunnelManagerError.swift | 40 | ||||
| -rw-r--r-- | ios/MullvadVPN/TunnelSettingsManager.swift | 19 |
12 files changed, 184 insertions, 62 deletions
diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj index 06b01ec444..15a2b8d6f3 100644 --- a/ios/MullvadVPN.xcodeproj/project.pbxproj +++ b/ios/MullvadVPN.xcodeproj/project.pbxproj @@ -79,6 +79,8 @@ 58293FB125124117005D0BB5 /* CustomTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58293FB025124117005D0BB5 /* CustomTextField.swift */; }; 58293FB3251241B4005D0BB5 /* CustomTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58293FB2251241B3005D0BB5 /* CustomTextView.swift */; }; 58293FB725138B88005D0BB5 /* CustomNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58293FB625138B88005D0BB5 /* CustomNavigationController.swift */; }; + 582AD44027BE616E002A6BFC /* CodingErrors+ChainedError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 582AD43F27BE616E002A6BFC /* CodingErrors+ChainedError.swift */; }; + 582AD44127BE6178002A6BFC /* CodingErrors+ChainedError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 582AD43F27BE616E002A6BFC /* CodingErrors+ChainedError.swift */; }; 582AE3102440A6CA00E6733A /* AccountTokenInput.swift in Sources */ = {isa = PBXBuildFile; fileRef = 582AE30F2440A6CA00E6733A /* AccountTokenInput.swift */; }; 582AE3122440CA0D00E6733A /* AccountTokenInputTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 582AE3112440CA0D00E6733A /* AccountTokenInputTests.swift */; }; 582AE3132440CA2700E6733A /* AccountTokenInput.swift in Sources */ = {isa = PBXBuildFile; fileRef = 582AE30F2440A6CA00E6733A /* AccountTokenInput.swift */; }; @@ -412,6 +414,7 @@ 58293FB025124117005D0BB5 /* CustomTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomTextField.swift; sourceTree = "<group>"; }; 58293FB2251241B3005D0BB5 /* CustomTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomTextView.swift; sourceTree = "<group>"; }; 58293FB625138B88005D0BB5 /* CustomNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomNavigationController.swift; sourceTree = "<group>"; }; + 582AD43F27BE616E002A6BFC /* CodingErrors+ChainedError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CodingErrors+ChainedError.swift"; sourceTree = "<group>"; }; 582AE30F2440A6CA00E6733A /* AccountTokenInput.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountTokenInput.swift; sourceTree = "<group>"; }; 582AE3112440CA0D00E6733A /* AccountTokenInputTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountTokenInputTests.swift; sourceTree = "<group>"; }; 582BB1AE229566420055B6EF /* SettingsCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsCell.swift; sourceTree = "<group>"; }; @@ -890,6 +893,7 @@ 5891BF1B25E3E3EB006D6FB0 /* Bundle+ProductVersion.swift */, 58F840B12464491D0044E708 /* ChainedError.swift */, 587EB669270EFACB00123C75 /* CharacterSet+IPAddress.swift */, + 582AD43F27BE616E002A6BFC /* CodingErrors+ChainedError.swift */, 58A1AA8B23F5584B009F7EA6 /* ConnectionPanelView.swift */, 58B43C1825F77DB60002C8C3 /* ConnectMainContentView.swift */, 58CCA00F224249A1004F3011 /* ConnectViewController.swift */, @@ -1367,6 +1371,7 @@ 58095C512760BBB500890776 /* AddressCacheTracker.swift in Sources */, 584D26C6270C8741004EA533 /* SettingsDNSTextCell.swift in Sources */, 585DA87D26B0254000B8C587 /* RelayCacheIO.swift in Sources */, + 582AD44027BE616E002A6BFC /* CodingErrors+ChainedError.swift in Sources */, 58F2E148276A307400A79513 /* MapConnectionStatusOperation.swift in Sources */, 58BA693123EADA6A009DC256 /* SimulatorTunnelProvider.swift in Sources */, 587C575326D2615F005EF767 /* PacketTunnelOptions.swift in Sources */, @@ -1567,6 +1572,7 @@ 587AD7C723421D8600E93A53 /* TunnelSettings.swift in Sources */, 5875960B26F3723000BF6711 /* TunnelIPC.swift in Sources */, 58AEEF662344A37400C9BBD5 /* KeychainError.swift in Sources */, + 582AD44127BE6178002A6BFC /* CodingErrors+ChainedError.swift in Sources */, 5840250222B1124600E4CFEC /* IPAddress+Codable.swift in Sources */, 5820675C26E6576800655B05 /* RelayCache.swift in Sources */, 58FAEDF1245069CA00CB0F5B /* KeychainAttributes.swift in Sources */, diff --git a/ios/MullvadVPN/Account.swift b/ios/MullvadVPN/Account.swift index 02f16c4fc8..54c5f06c0b 100644 --- a/ios/MullvadVPN/Account.swift +++ b/ios/MullvadVPN/Account.swift @@ -60,6 +60,17 @@ class Account { /// A failure to configure a tunnel case tunnelConfiguration(TunnelManager.Error) + + var errorDescription: String? { + switch self { + case .createAccount: + return "Failure to create new account." + case .verifyAccount: + return "Failure to verify account." + case .tunnelConfiguration: + return "Failure to configure the tunnel." + } + } } /// A shared instance of `Account` diff --git a/ios/MullvadVPN/AppStoreReceipt.swift b/ios/MullvadVPN/AppStoreReceipt.swift index d39d58cf25..995c17e719 100644 --- a/ios/MullvadVPN/AppStoreReceipt.swift +++ b/ios/MullvadVPN/AppStoreReceipt.swift @@ -23,11 +23,11 @@ enum AppStoreReceipt { var errorDescription: String? { switch self { case .doesNotExist: - return "AppStore receipt file does not exist on disk" + return "AppStore receipt file does not exist on disk." case .io: - return "Read error" + return "Read error." case .refresh: - return "Receipt refresh error" + return "Receipt refresh error." } } } diff --git a/ios/MullvadVPN/ChainedError.swift b/ios/MullvadVPN/ChainedError.swift index 86977609a1..446ed99469 100644 --- a/ios/MullvadVPN/ChainedError.swift +++ b/ios/MullvadVPN/ChainedError.swift @@ -8,26 +8,19 @@ import Foundation -/// A protocol describing errors that can be chained together +/// A protocol describing errors that can be chained together. protocol ChainedError: LocalizedError { - /// A source error when available + /// A source error when available. var source: Error? { get } } -final class AnyChainedError: ChainedError { - private let wrappedError: Error - - init(_ error: Error) { - wrappedError = error - } - - var errorDescription: String? { - return wrappedError.localizedDescription - } +/// A protocol providing error a way to override error description when printing error chain. +protocol CustomChainedErrorDescriptionProtocol { + /// A custom error description that overrides `localizedDescription` when printing error chain. + var customErrorDescription: String? { get } } extension ChainedError { - var source: Error? { let reflection = Mirror(reflecting: self) @@ -42,14 +35,20 @@ extension ChainedError { return nil } - /// Creates a string representation of the entire error chain. - /// An extra `message` is added at the start of the chain when given + /// Create a string representation of the entire error chain. + /// An extra `message` is added at the start of the chain when given. func displayChain(message: String? = nil) -> String { - var s = message.map { "Error: \($0)\nCaused by: \(self.localizedDescription)" } - ?? "Error: \(self.localizedDescription)" + var s: String + + let errorDescription = Self.getErrorDescription(self) + if let message = message { + s = "Error: \(message)\nCaused by: \(errorDescription)" + } else { + s = "Error: \(errorDescription)" + } for sourceError in makeChainIterator() { - s.append("\nCaused by: \(sourceError.localizedDescription)") + s.append("\nCaused by: \(Self.getErrorDescription(sourceError))") } return s @@ -62,4 +61,38 @@ extension ChainedError { return current } } + + private static func getErrorDescription(_ error: Error) -> String { + let anError = error as? CustomChainedErrorDescriptionProtocol + + return anError?.customErrorDescription ?? error.localizedDescription + } +} + +extension CustomChainedErrorDescriptionProtocol { + var customErrorDescription: String? { + return nil + } +} + +/// A type-erasing container type for any `Error` that makes the wrapped error behave like +/// `ChainedError`. +final class AnyChainedError: ChainedError, CustomChainedErrorDescriptionProtocol { + private let wrappedError: Error + + init(_ error: Error) { + wrappedError = error + } + + var source: Error? { + return (wrappedError as? ChainedError)?.source + } + + var errorDescription: String? { + return wrappedError.localizedDescription + } + + var customErrorDescription: String? { + return (wrappedError as? CustomChainedErrorDescriptionProtocol)?.customErrorDescription + } } diff --git a/ios/MullvadVPN/CodingErrors+ChainedError.swift b/ios/MullvadVPN/CodingErrors+ChainedError.swift new file mode 100644 index 0000000000..e14d61473b --- /dev/null +++ b/ios/MullvadVPN/CodingErrors+ChainedError.swift @@ -0,0 +1,53 @@ +// +// CodingErrors+ChainedError.swift +// MullvadVPN +// +// Created by pronebird on 17/02/2022. +// Copyright © 2022 Mullvad VPN AB. All rights reserved. +// + +import Foundation + +extension DecodingError: CustomChainedErrorDescriptionProtocol { + var customErrorDescription: String? { + switch self { + case .typeMismatch(let type, let context): + return "Type mismatch, expected \(type) for key at \"\(context.codingPath.codingPathString)\"." + + case .valueNotFound(_, let context): + return "Value not found at \"\(context.codingPath.codingPathString)\"." + + case .keyNotFound(let codingKey, let context): + return "Key \"\(codingKey.stringValue)\" not found at \"\(context.codingPath.codingPathString)\"." + + case .dataCorrupted: + return "Data corrupted." + + @unknown default: + return nil + } + } +} + +extension EncodingError: CustomChainedErrorDescriptionProtocol { + var customErrorDescription: String? { + switch self { + case .invalidValue(_, let context): + return "Invalid value at \"\(context.codingPath.codingPathString)\"" + + @unknown default: + return nil + } + } +} + +private extension Array where Element == CodingKey { + var codingPathString: String { + if isEmpty { + return "<root>" + } else { + return map { $0.stringValue } + .joined(separator: ".") + } + } +} diff --git a/ios/MullvadVPN/ConsolidatedApplicationLog.swift b/ios/MullvadVPN/ConsolidatedApplicationLog.swift index e994b4237e..e31b6b6371 100644 --- a/ios/MullvadVPN/ConsolidatedApplicationLog.swift +++ b/ios/MullvadVPN/ConsolidatedApplicationLog.swift @@ -35,9 +35,9 @@ class ConsolidatedApplicationLog: TextOutputStreamable { var errorDescription: String? { switch self { case .logFileDoesNotExist(let path): - return "Log file does not exist: \(path)" + return "Log file does not exist: \(path)." case .invalidLogFileURL(let url): - return "Invalid log file URL: \(url.absoluteString)" + return "Invalid log file URL: \(url.absoluteString)." } } } diff --git a/ios/MullvadVPN/Logging/LogRotation.swift b/ios/MullvadVPN/Logging/LogRotation.swift index fdc66c20b6..696f7c4a41 100644 --- a/ios/MullvadVPN/Logging/LogRotation.swift +++ b/ios/MullvadVPN/Logging/LogRotation.swift @@ -17,9 +17,9 @@ enum LogRotation { var errorDescription: String? { switch self { case .noSourceLogFile: - return "Source log file does not exist" + return "Source log file does not exist." case .moveSourceLogFile: - return "Failure to move the source log file to backup" + return "Failure to move the source log file to backup." } } } diff --git a/ios/MullvadVPN/REST/RESTError.swift b/ios/MullvadVPN/REST/RESTError.swift index 110eafbfcd..6d13a6e6f9 100644 --- a/ios/MullvadVPN/REST/RESTError.swift +++ b/ios/MullvadVPN/REST/RESTError.swift @@ -30,15 +30,15 @@ extension REST { var errorDescription: String? { switch self { case .encodePayload: - return "Failure to encode the payload" + return "Failure to encode the payload." case .network: - return "Network error" + return "Network error." case .server: - return "Server error" + return "Server error." case .decodeErrorResponse: - return "Failure to decode error response from server" + return "Failure to decode error response from server." case .decodeSuccessResponse: - return "Failure to decode success response from server" + return "Failure to decode success response from server." } } } diff --git a/ios/MullvadVPN/RelayCache/RelayCacheError.swift b/ios/MullvadVPN/RelayCache/RelayCacheError.swift index c161ee3937..9e18643a21 100644 --- a/ios/MullvadVPN/RelayCache/RelayCacheError.swift +++ b/ios/MullvadVPN/RelayCache/RelayCacheError.swift @@ -24,21 +24,21 @@ extension RelayCache { var errorDescription: String? { switch self { case .encodeCache: - return "Encode cache error" + return "Encode cache error." case .decodeCache: - return "Decode cache error" + return "Decode cache error." case .readCache: - return "Read cache error" + return "Read cache error." case .readPrebundledRelays: - return "Read pre-bundled relays error" + return "Read pre-bundled relays error." case .decodePrebundledRelays: - return "Decode pre-bundled relays error" + return "Decode pre-bundled relays error." case .writeCache: - return "Write cache error" + return "Write cache error." case .rest: - return "REST error" + return "REST error." case .backgroundTaskScheduler: - return "Background task scheduler error" + return "Background task scheduler error." } } } diff --git a/ios/MullvadVPN/TunnelIPC/TunnelIPCError.swift b/ios/MullvadVPN/TunnelIPC/TunnelIPCError.swift index b2af509c80..29cd73c4e4 100644 --- a/ios/MullvadVPN/TunnelIPC/TunnelIPCError.swift +++ b/ios/MullvadVPN/TunnelIPC/TunnelIPCError.swift @@ -28,13 +28,13 @@ extension TunnelIPC { var errorDescription: String? { switch self { case .encoding: - return "Encoding failure" + return "Encoding failure." case .decoding: - return "Decoding failure" + return "Decoding failure." case .send: - return "Send failure" + return "Send failure." case .nilResponse: - return "Unexpected nil response from the tunnel" + return "Unexpected nil response from the tunnel." } } } diff --git a/ios/MullvadVPN/TunnelManager/TunnelManagerError.swift b/ios/MullvadVPN/TunnelManager/TunnelManagerError.swift index e60e3a3dd5..4f0b346417 100644 --- a/ios/MullvadVPN/TunnelManager/TunnelManagerError.swift +++ b/ios/MullvadVPN/TunnelManager/TunnelManagerError.swift @@ -75,45 +75,45 @@ extension TunnelManager { var errorDescription: String? { switch self { case .unsetAccount: - return "Account is unset" + return "Account is unset." case .startVPNTunnel: - return "Failed to start the VPN tunnel" + return "Failed to start the VPN tunnel." case .loadAllVPNConfigurations: - return "Failed to load the system VPN configurations" + return "Failed to load the system VPN configurations." case .saveVPNConfiguration: - return "Failed to save the system VPN configuration" + return "Failed to save the system VPN configuration." case .reloadVPNConfiguration: - return "Failed to reload the system VPN configuration" + return "Failed to reload the system VPN configuration." case .removeVPNConfiguration: - return "Failed to remove the system VPN configuration" + return "Failed to remove the system VPN configuration." case .removeInconsistentVPNConfiguration: - return "Failed to remove the inconsistent VPN tunnel" + return "Failed to remove the inconsistent VPN tunnel." case .readTunnelSettings: - return "Failed to read the tunnel settings" + return "Failed to read the tunnel settings." case .readRelays: - return "Failed to read relays" + return "Failed to read relays." case .cannotSatisfyRelayConstraints: - return "Failed to satisfy the relay constraints" + return "Failed to satisfy the relay constraints." case .addTunnelSettings: - return "Failed to add the tunnel settings" + return "Failed to add the tunnel settings." case .updateTunnelSettings: - return "Failed to update the tunnel settings" + return "Failed to update the tunnel settings." case .removeTunnelSettings: - return "Failed to remove the tunnel settings" + return "Failed to remove the tunnel settings." case .migrateTunnelSettings: - return "Failed to migrate the tunnel settings" + return "Failed to migrate the tunnel settings." case .obtainPersistentKeychainReference: - return "Failed to obtain the persistent keychain reference" + return "Failed to obtain the persistent keychain reference." case .pushWireguardKey: - return "Failed to push the WireGuard key to server" + return "Failed to push the WireGuard key to server." case .replaceWireguardKey: - return "Failed to replace the WireGuard key on server" + return "Failed to replace the WireGuard key on server." case .removeWireguardKey: - return "Failed to remove the WireGuard key from server" + return "Failed to remove the WireGuard key from server." case .backgroundTaskScheduler: - return "Failed to schedule background task" + return "Failed to schedule background task." case .reloadTunnel: - return "Failed to reload tunnel" + return "Failed to reload tunnel." } } } diff --git a/ios/MullvadVPN/TunnelSettingsManager.swift b/ios/MullvadVPN/TunnelSettingsManager.swift index c3fb458217..b4aec2b8ba 100644 --- a/ios/MullvadVPN/TunnelSettingsManager.swift +++ b/ios/MullvadVPN/TunnelSettingsManager.swift @@ -37,6 +37,25 @@ extension TunnelSettingsManager { /// Missing attributes required to perform an operation. case missingRequiredAttributes + + var errorDescription: String? { + switch self { + case .encode: + return "Failure to encode settings." + case .decode: + return "Failure to decode settings." + case .addEntry: + return "Failure to add keychain entry." + case .updateEntry: + return "Failure to update keychain entry." + case .removeEntry: + return "Failure to remove keychain entry." + case .lookupEntry: + return "Failure to lookup keychain entry." + case .missingRequiredAttributes: + return "Keychain entry is missing required set of attributes." + } + } } typealias Result<T> = Swift.Result<T, Error> |
