summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAndrej Mihajlov <and@mullvad.net>2022-02-22 11:24:29 +0100
committerAndrej Mihajlov <and@mullvad.net>2022-02-22 11:24:29 +0100
commit7aea025585bf95654bdd216c48edd3769057aba0 (patch)
tree62a3d6630bc24bee9fcac9fac52c8cc31592651e
parent1f39933d4881569fc8fb62fdb8ca322ee6af5593 (diff)
parentdcb8a8a29734dce970025c122c3402640fd215ae (diff)
downloadmullvadvpn-7aea025585bf95654bdd216c48edd3769057aba0.tar.xz
mullvadvpn-7aea025585bf95654bdd216c48edd3769057aba0.zip
Merge branch 'improve-error-output'
-rw-r--r--ios/MullvadVPN.xcodeproj/project.pbxproj6
-rw-r--r--ios/MullvadVPN/Account.swift11
-rw-r--r--ios/MullvadVPN/AppStoreReceipt.swift6
-rw-r--r--ios/MullvadVPN/ChainedError.swift69
-rw-r--r--ios/MullvadVPN/CodingErrors+ChainedError.swift53
-rw-r--r--ios/MullvadVPN/ConsolidatedApplicationLog.swift4
-rw-r--r--ios/MullvadVPN/Logging/LogRotation.swift4
-rw-r--r--ios/MullvadVPN/REST/RESTError.swift10
-rw-r--r--ios/MullvadVPN/RelayCache/RelayCacheError.swift16
-rw-r--r--ios/MullvadVPN/TunnelIPC/TunnelIPCError.swift8
-rw-r--r--ios/MullvadVPN/TunnelManager/TunnelManagerError.swift40
-rw-r--r--ios/MullvadVPN/TunnelSettingsManager.swift19
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>