summaryrefslogtreecommitdiffhomepage
path: root/ios/MullvadVPN
diff options
context:
space:
mode:
authorAndrej Mihajlov <and@mullvad.net>2022-09-26 13:46:17 +0200
committerAndrej Mihajlov <and@mullvad.net>2022-09-26 13:46:17 +0200
commit5a7cb538c6b71af5117efef273d8ea3845788517 (patch)
tree4028c8d42bd18f73c61f55f36a9f2a267dcd8249 /ios/MullvadVPN
parent0bff2bee9cf3eae8e55138d7af3cf7edcbea5fa0 (diff)
parent324556c1ef5ff6aca3ed331b71de0a30b0567032 (diff)
downloadmullvadvpn-5a7cb538c6b71af5117efef273d8ea3845788517.tar.xz
mullvadvpn-5a7cb538c6b71af5117efef273d8ea3845788517.zip
Merge branch 'remove-chained-error'
Diffstat (limited to 'ios/MullvadVPN')
-rw-r--r--ios/MullvadVPN/AddressCache/AddressCacheStore.swift10
-rw-r--r--ios/MullvadVPN/AddressCache/AddressCacheTracker.swift2
-rw-r--r--ios/MullvadVPN/AppDelegate.swift6
-rw-r--r--ios/MullvadVPN/AppStorePaymentManager/AppStorePaymentManagerError.swift27
-rw-r--r--ios/MullvadVPN/AppStorePaymentManager/SendAppStoreReceiptOperation.swift4
-rw-r--r--ios/MullvadVPN/ChainedError.swift98
-rw-r--r--ios/MullvadVPN/CodingErrors+CustomErrorDescription.swift (renamed from ios/MullvadVPN/CodingErrors+ChainedError.swift)6
-rw-r--r--ios/MullvadVPN/ConnectViewController.swift2
-rw-r--r--ios/MullvadVPN/CustomErrorDescriptionProtocol.swift15
-rw-r--r--ios/MullvadVPN/DeviceManagementViewController.swift2
-rw-r--r--ios/MullvadVPN/Error+Chain.swift44
-rw-r--r--ios/MullvadVPN/Logging/LogRotation.swift27
-rw-r--r--ios/MullvadVPN/Logging/Logger+Errors.swift (renamed from ios/MullvadVPN/Logging/ChainedError+Logger.swift)26
-rw-r--r--ios/MullvadVPN/Logging/Logging.swift2
-rw-r--r--ios/MullvadVPN/LoginViewController.swift4
-rw-r--r--ios/MullvadVPN/NotificationManager.swift4
-rw-r--r--ios/MullvadVPN/Notifications/TunnelStatusNotificationProvider.swift4
-rw-r--r--ios/MullvadVPN/REST/RESTAccessTokenManager.swift2
-rw-r--r--ios/MullvadVPN/REST/RESTError.swift27
-rw-r--r--ios/MullvadVPN/REST/RESTNetworkOperation.swift6
-rw-r--r--ios/MullvadVPN/RelayCache/RelayCacheTracker.swift4
-rw-r--r--ios/MullvadVPN/SettingsManager/SettingsManager.swift6
-rw-r--r--ios/MullvadVPN/ShortcutsManager.swift2
-rw-r--r--ios/MullvadVPN/SimulatorTunnelProviderHost.swift6
-rw-r--r--ios/MullvadVPN/TunnelManager/LoadTunnelConfigurationOperation.swift10
-rw-r--r--ios/MullvadVPN/TunnelManager/MigrateSettingsOperation.swift10
-rw-r--r--ios/MullvadVPN/TunnelManager/RotateKeyOperation.swift2
-rw-r--r--ios/MullvadVPN/TunnelManager/SendTunnelProviderMessageOperation.swift15
-rw-r--r--ios/MullvadVPN/TunnelManager/SetAccountOperation.swift12
-rw-r--r--ios/MullvadVPN/TunnelManager/StartTunnelOperation.swift2
-rw-r--r--ios/MullvadVPN/TunnelManager/TunnelManager.swift12
-rw-r--r--ios/MullvadVPN/TunnelManager/TunnelManagerErrors.swift22
-rw-r--r--ios/MullvadVPN/TunnelManager/UpdateAccountDataOperation.swift2
-rw-r--r--ios/MullvadVPN/WrappingError.swift14
34 files changed, 235 insertions, 202 deletions
diff --git a/ios/MullvadVPN/AddressCache/AddressCacheStore.swift b/ios/MullvadVPN/AddressCache/AddressCacheStore.swift
index 94ea5d5850..57e5a9bed8 100644
--- a/ios/MullvadVPN/AddressCache/AddressCacheStore.swift
+++ b/ios/MullvadVPN/AddressCache/AddressCacheStore.swift
@@ -121,7 +121,7 @@ extension AddressCache {
try writeToDisk()
} catch {
logger.error(
- chainedError: AnyChainedError(error),
+ error: error,
message: "Failed to persist address cache after reading it from bundle."
)
}
@@ -170,7 +170,7 @@ extension AddressCache {
try writeToDisk()
} catch {
logger.error(
- chainedError: AnyChainedError(error),
+ error: error,
message: "Failed to write address cache after selecting next endpoint."
)
}
@@ -209,7 +209,7 @@ extension AddressCache {
try writeToDisk()
} catch {
logger.error(
- chainedError: AnyChainedError(error),
+ error: error,
message: "Failed to write address cache after setting new endpoints."
)
}
@@ -238,7 +238,7 @@ extension AddressCache {
return readResult
} catch {
logger.error(
- chainedError: AnyChainedError(error),
+ error: error,
message: "Failed to read address cache from disk. Fallback to pre-bundled cache."
)
@@ -253,7 +253,7 @@ extension AddressCache {
return readResult
} catch {
logger.error(
- chainedError: AnyChainedError(error),
+ error: error,
message: "Failed to read address cache from bundle."
)
diff --git a/ios/MullvadVPN/AddressCache/AddressCacheTracker.swift b/ios/MullvadVPN/AddressCache/AddressCacheTracker.swift
index 69300772ac..00d118de75 100644
--- a/ios/MullvadVPN/AddressCache/AddressCacheTracker.swift
+++ b/ios/MullvadVPN/AddressCache/AddressCacheTracker.swift
@@ -143,7 +143,7 @@ extension AddressCache {
case let .failure(error):
logger.error(
- chainedError: AnyChainedError(error),
+ error: error,
message: "Failed to update address cache."
)
diff --git a/ios/MullvadVPN/AppDelegate.swift b/ios/MullvadVPN/AppDelegate.swift
index 74670008a7..10d44fd99b 100644
--- a/ios/MullvadVPN/AppDelegate.swift
+++ b/ios/MullvadVPN/AppDelegate.swift
@@ -317,7 +317,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
try BGTaskScheduler.shared.submit(request)
} catch {
logger.error(
- chainedError: AnyChainedError(error),
+ error: error,
message: "Could not schedule app refresh task."
)
}
@@ -341,7 +341,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
try BGTaskScheduler.shared.submit(request)
} catch {
logger.error(
- chainedError: AnyChainedError(error),
+ error: error,
message: "Could not schedule private key rotation task."
)
}
@@ -363,7 +363,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
try BGTaskScheduler.shared.submit(request)
} catch {
logger.error(
- chainedError: AnyChainedError(error),
+ error: error,
message: "Could not schedule address cache update task."
)
}
diff --git a/ios/MullvadVPN/AppStorePaymentManager/AppStorePaymentManagerError.swift b/ios/MullvadVPN/AppStorePaymentManager/AppStorePaymentManagerError.swift
index ad29ef3929..f88b6255d4 100644
--- a/ios/MullvadVPN/AppStorePaymentManager/AppStorePaymentManagerError.swift
+++ b/ios/MullvadVPN/AppStorePaymentManager/AppStorePaymentManagerError.swift
@@ -10,7 +10,7 @@ import Foundation
extension AppStorePaymentManager {
/// An error type emitted by `AppStorePaymentManager`.
- enum Error: ChainedError {
+ enum Error: LocalizedError, WrappingError {
/// Failure to find the account token associated with the transaction.
case noAccountSet
@@ -29,15 +29,30 @@ extension AppStorePaymentManager {
var errorDescription: String? {
switch self {
case .noAccountSet:
- return "Account is not set"
+ return "Account is not set."
case .validateAccount:
- return "Account validation error"
+ return "Account validation error."
case .storePayment:
- return "Store payment error"
+ return "Store payment error."
case .readReceipt:
- return "Read recept error"
+ return "Read recept error."
case .sendReceipt:
- return "Send receipt error"
+ return "Send receipt error."
+ }
+ }
+
+ var underlyingError: Swift.Error? {
+ switch self {
+ case .noAccountSet:
+ return nil
+ case let .sendReceipt(error):
+ return error
+ case let .validateAccount(error):
+ return error
+ case let .readReceipt(error):
+ return error
+ case let .storePayment(error):
+ return error
}
}
}
diff --git a/ios/MullvadVPN/AppStorePaymentManager/SendAppStoreReceiptOperation.swift b/ios/MullvadVPN/AppStorePaymentManager/SendAppStoreReceiptOperation.swift
index 32295d4350..b99c542c5a 100644
--- a/ios/MullvadVPN/AppStorePaymentManager/SendAppStoreReceiptOperation.swift
+++ b/ios/MullvadVPN/AppStorePaymentManager/SendAppStoreReceiptOperation.swift
@@ -60,7 +60,7 @@ class SendAppStoreReceiptOperation: ResultOperation<
case let .failure(error):
self.logger.error(
- chainedError: AnyChainedError(error),
+ error: error,
message: "Failed to fetch the AppStore receipt."
)
self.finish(completion: .failure(.readReceipt(error)))
@@ -87,7 +87,7 @@ class SendAppStoreReceiptOperation: ResultOperation<
case let .failure(error):
self.logger.error(
- chainedError: error,
+ error: error,
message: "Failed to send the AppStore receipt."
)
self.finish(completion: .failure(.sendReceipt(error)))
diff --git a/ios/MullvadVPN/ChainedError.swift b/ios/MullvadVPN/ChainedError.swift
deleted file mode 100644
index 446ed99469..0000000000
--- a/ios/MullvadVPN/ChainedError.swift
+++ /dev/null
@@ -1,98 +0,0 @@
-//
-// ErrorChain.swift
-// MullvadVPN
-//
-// Created by pronebird on 07/05/2020.
-// Copyright © 2020 Mullvad VPN AB. All rights reserved.
-//
-
-import Foundation
-
-/// A protocol describing errors that can be chained together.
-protocol ChainedError: LocalizedError {
- /// A source error when available.
- var source: Error? { get }
-}
-
-/// 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)
-
- if case .enum = reflection.displayStyle {
- for child in reflection.children {
- if let associatedError = child.value as? Error {
- return associatedError
- }
- }
- }
-
- return nil
- }
-
- /// 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: 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: \(Self.getErrorDescription(sourceError))")
- }
-
- return s
- }
-
- private func makeChainIterator() -> AnyIterator<Error> {
- var current: Error? = self
- return AnyIterator { () -> Error? in
- current = (current as? ChainedError)?.source
- 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+CustomErrorDescription.swift
index 1dfa73f923..25c65d8b82 100644
--- a/ios/MullvadVPN/CodingErrors+ChainedError.swift
+++ b/ios/MullvadVPN/CodingErrors+CustomErrorDescription.swift
@@ -1,5 +1,5 @@
//
-// CodingErrors+ChainedError.swift
+// CodingErrors+CustomErrorDescription.swift
// MullvadVPN
//
// Created by pronebird on 17/02/2022.
@@ -8,7 +8,7 @@
import Foundation
-extension DecodingError: CustomChainedErrorDescriptionProtocol {
+extension DecodingError: CustomErrorDescriptionProtocol {
var customErrorDescription: String? {
switch self {
case let .typeMismatch(type, context):
@@ -29,7 +29,7 @@ extension DecodingError: CustomChainedErrorDescriptionProtocol {
}
}
-extension EncodingError: CustomChainedErrorDescriptionProtocol {
+extension EncodingError: CustomErrorDescriptionProtocol {
var customErrorDescription: String? {
switch self {
case let .invalidValue(_, context):
diff --git a/ios/MullvadVPN/ConnectViewController.swift b/ios/MullvadVPN/ConnectViewController.swift
index 49a7bdf1bf..e3da0fe159 100644
--- a/ios/MullvadVPN/ConnectViewController.swift
+++ b/ios/MullvadVPN/ConnectViewController.swift
@@ -536,7 +536,7 @@ class ConnectViewController: UIViewController, MKMapViewDelegate, RootContainmen
contentView.mapView.addOverlays(overlays, level: .aboveLabels)
} catch {
- logger.error(chainedError: AnyChainedError(error), message: "Failed to load geojson.")
+ logger.error(error: error, message: "Failed to load geojson.")
}
}
}
diff --git a/ios/MullvadVPN/CustomErrorDescriptionProtocol.swift b/ios/MullvadVPN/CustomErrorDescriptionProtocol.swift
new file mode 100644
index 0000000000..448d848686
--- /dev/null
+++ b/ios/MullvadVPN/CustomErrorDescriptionProtocol.swift
@@ -0,0 +1,15 @@
+//
+// CustomErrorDescription.swift
+// MullvadVPN
+//
+// Created by pronebird on 23/09/2022.
+// Copyright © 2022 Mullvad VPN AB. All rights reserved.
+//
+
+import Foundation
+
+/// A protocol providing error a way to override error description when printing error chain.
+protocol CustomErrorDescriptionProtocol {
+ /// A custom error description that overrides `localizedDescription` when printing error chain.
+ var customErrorDescription: String? { get }
+}
diff --git a/ios/MullvadVPN/DeviceManagementViewController.swift b/ios/MullvadVPN/DeviceManagementViewController.swift
index 128e1c6ab4..383b7ca822 100644
--- a/ios/MullvadVPN/DeviceManagementViewController.swift
+++ b/ios/MullvadVPN/DeviceManagementViewController.swift
@@ -248,7 +248,7 @@ class DeviceManagementViewController: UIViewController, RootContainment {
case let .failure(error):
self.logger.error(
- chainedError: AnyChainedError(error),
+ error: error,
message: "Failed to delete device."
)
completionHandler(error)
diff --git a/ios/MullvadVPN/Error+Chain.swift b/ios/MullvadVPN/Error+Chain.swift
new file mode 100644
index 0000000000..8ddd0df219
--- /dev/null
+++ b/ios/MullvadVPN/Error+Chain.swift
@@ -0,0 +1,44 @@
+//
+// Error+Chain.swift
+// MullvadVPN
+//
+// Created by pronebird on 23/09/2022.
+// Copyright © 2022 Mullvad VPN AB. All rights reserved.
+//
+
+import Foundation
+
+extension Error {
+ /// Returns a flat list of errors by unrolling the underlying error chain.
+ var underlyingErrorChain: [Error] {
+ var errors: [Error] = []
+ var currentError: Error? = self as Error
+
+ while let underlyingError = currentError?.getUnderlyingError() {
+ currentError = underlyingError
+ errors.append(underlyingError)
+ }
+
+ return errors
+ }
+
+ func logFormatError() -> String {
+ let nsError = self as NSError
+ var message = ""
+
+ let description = (self as? CustomErrorDescriptionProtocol)?
+ .customErrorDescription ?? localizedDescription
+
+ message += "\(description) (domain = \(nsError.domain), code = \(nsError.code))"
+
+ return message
+ }
+
+ private func getUnderlyingError() -> Error? {
+ if let wrappingError = self as? WrappingError {
+ return wrappingError.underlyingError
+ } else {
+ return (self as NSError).userInfo[NSUnderlyingErrorKey] as? Error
+ }
+ }
+}
diff --git a/ios/MullvadVPN/Logging/LogRotation.swift b/ios/MullvadVPN/Logging/LogRotation.swift
index ff7671ae80..a4c3be01f5 100644
--- a/ios/MullvadVPN/Logging/LogRotation.swift
+++ b/ios/MullvadVPN/Logging/LogRotation.swift
@@ -9,7 +9,7 @@
import Foundation
enum LogRotation {
- enum Error: ChainedError {
+ enum Error: LocalizedError, WrappingError {
case noSourceLogFile
case moveSourceLogFile(Swift.Error)
@@ -21,30 +21,27 @@ enum LogRotation {
return "Failure to move the source log file to backup."
}
}
+
+ var underlyingError: Swift.Error? {
+ switch self {
+ case .noSourceLogFile:
+ return nil
+ case let .moveSourceLogFile(error):
+ return error
+ }
+ }
}
static func rotateLog(logsDirectory: URL, logFileName: String) throws {
- let fileManager = FileManager.default
let source = logsDirectory.appendingPathComponent(logFileName)
let backup = source.deletingPathExtension().appendingPathExtension("old.log")
do {
- _ = try fileManager.replaceItemAt(backup, withItemAt: source)
+ _ = try FileManager.default.replaceItemAt(backup, withItemAt: source)
} catch {
// FileManager returns a very obscure error chain so we need to traverse it to find
// the root cause of the error.
- var errorCursor: Swift.Error? = error
- let cocoaErrorIterator = AnyIterator { () -> CocoaError? in
- if let cocoaError = errorCursor as? CocoaError {
- errorCursor = cocoaError.underlying
- return cocoaError
- } else {
- errorCursor = nil
- return nil
- }
- }
-
- while let fileError = cocoaErrorIterator.next() {
+ for case let fileError as CocoaError in error.underlyingErrorChain {
// .fileNoSuchFile is returned when both backup and source log files do not exist
// .fileReadNoSuchFile is returned when backup exists but source log file does not
if fileError.code == .fileNoSuchFile || fileError.code == .fileReadNoSuchFile,
diff --git a/ios/MullvadVPN/Logging/ChainedError+Logger.swift b/ios/MullvadVPN/Logging/Logger+Errors.swift
index 0aede67701..0b2811a68e 100644
--- a/ios/MullvadVPN/Logging/ChainedError+Logger.swift
+++ b/ios/MullvadVPN/Logging/Logger+Errors.swift
@@ -1,5 +1,5 @@
//
-// ChainedError+Logger.swift
+// Logger+Errors.swift
// MullvadVPN
//
// Created by pronebird on 02/08/2020.
@@ -10,8 +10,8 @@ import Foundation
import Logging
extension Logger {
- func error<T: ChainedError>(
- chainedError: T,
+ func error<T: Error>(
+ error: T,
message: @autoclosure () -> String? = nil,
metadata: @autoclosure () -> Logger.Metadata? = nil,
source: @autoclosure () -> String? = nil,
@@ -19,11 +19,25 @@ extension Logger {
function: String = #function,
line: UInt = #line
) {
+ var lines = [String]()
+ var errors = [Error]()
+
+ if let prefixMessage = message() {
+ lines.append(prefixMessage)
+ errors.append(error)
+ } else {
+ lines.append(error.logFormatError())
+ }
+
+ errors.append(contentsOf: error.underlyingErrorChain)
+
+ for error in errors {
+ lines.append("Caused by: \(error.logFormatError())")
+ }
+
log(
level: .error,
- Message(
- stringLiteral: chainedError.displayChain(message: message())
- ),
+ Message(stringLiteral: lines.joined(separator: "\n")),
metadata: metadata(),
source: source(),
file: file,
diff --git a/ios/MullvadVPN/Logging/Logging.swift b/ios/MullvadVPN/Logging/Logging.swift
index a0c535853e..1e65249ec8 100644
--- a/ios/MullvadVPN/Logging/Logging.swift
+++ b/ios/MullvadVPN/Logging/Logging.swift
@@ -70,7 +70,7 @@ func initLoggingSystem(bundleIdentifier: String, metadata: Logger.Metadata? = ni
if let logRotationError = logRotationError {
Logger(label: "LogRotation").error(
- chainedError: AnyChainedError(logRotationError),
+ error: logRotationError,
message: "Failed to rotate log"
)
}
diff --git a/ios/MullvadVPN/LoginViewController.swift b/ios/MullvadVPN/LoginViewController.swift
index 6e363a6b25..b0587e9543 100644
--- a/ios/MullvadVPN/LoginViewController.swift
+++ b/ios/MullvadVPN/LoginViewController.swift
@@ -236,7 +236,7 @@ class LoginViewController: UIViewController, RootContainment {
contentView.accountInputGroup.setLastUsedAccount(accountNumber, animated: false)
} catch {
logger.error(
- chainedError: AnyChainedError(error),
+ error: error,
message: "Failed to update last used account."
)
}
@@ -423,7 +423,7 @@ extension LoginViewController: AccountInputGroupViewDelegate {
return true
} catch {
logger.error(
- chainedError: AnyChainedError(error),
+ error: error,
message: "Failed to remove last used account."
)
return false
diff --git a/ios/MullvadVPN/NotificationManager.swift b/ios/MullvadVPN/NotificationManager.swift
index 63468e70a3..5838b41d5b 100644
--- a/ios/MullvadVPN/NotificationManager.swift
+++ b/ios/MullvadVPN/NotificationManager.swift
@@ -145,7 +145,7 @@ class NotificationManager: NotificationProviderDelegate {
notificationCenter.add(newRequest) { error in
if let error = error {
self.logger.error(
- chainedError: AnyChainedError(error),
+ error: error,
message: "Failed to add notification request with identifier \(newRequest.identifier)."
)
}
@@ -174,7 +174,7 @@ class NotificationManager: NotificationProviderDelegate {
.requestAuthorization(options: authorizationOptions) { granted, error in
if let error = error {
self.logger.error(
- chainedError: AnyChainedError(error),
+ error: error,
message: "Failed to obtain user notifications authorization"
)
}
diff --git a/ios/MullvadVPN/Notifications/TunnelStatusNotificationProvider.swift b/ios/MullvadVPN/Notifications/TunnelStatusNotificationProvider.swift
index d19335972d..59642ccc0c 100644
--- a/ios/MullvadVPN/Notifications/TunnelStatusNotificationProvider.swift
+++ b/ios/MullvadVPN/Notifications/TunnelStatusNotificationProvider.swift
@@ -124,7 +124,7 @@ class TunnelStatusNotificationProvider: NotificationProvider, InAppNotificationP
value: "Failed to start the tunnel: %@.",
comment: ""
),
- startError.underlyingError.localizedDescription
+ startError.underlyingError?.localizedDescription ?? ""
)
} else if let stopError = error as? StopTunnelError {
body = String(
@@ -133,7 +133,7 @@ class TunnelStatusNotificationProvider: NotificationProvider, InAppNotificationP
value: "Failed to stop the tunnel: %@.",
comment: ""
),
- stopError.underlyingError.localizedDescription
+ stopError.underlyingError?.localizedDescription ?? ""
)
} else {
body = error.localizedDescription
diff --git a/ios/MullvadVPN/REST/RESTAccessTokenManager.swift b/ios/MullvadVPN/REST/RESTAccessTokenManager.swift
index 7d46b16edd..79e5096fb5 100644
--- a/ios/MullvadVPN/REST/RESTAccessTokenManager.swift
+++ b/ios/MullvadVPN/REST/RESTAccessTokenManager.swift
@@ -49,7 +49,7 @@ extension REST {
case let .failure(error):
self.logger.error(
- chainedError: error,
+ error: error,
message: "Failed to fetch access token."
)
diff --git a/ios/MullvadVPN/REST/RESTError.swift b/ios/MullvadVPN/REST/RESTError.swift
index 25e51bd1b9..63a6167d39 100644
--- a/ios/MullvadVPN/REST/RESTError.swift
+++ b/ios/MullvadVPN/REST/RESTError.swift
@@ -10,7 +10,7 @@ import Foundation
extension REST {
/// An error type returned by REST API classes.
- enum Error: ChainedError {
+ enum Error: LocalizedError, WrappingError {
/// A failure to create URL request.
case createURLRequest(Swift.Error)
@@ -25,10 +25,10 @@ extension REST {
var errorDescription: String? {
switch self {
- case .createURLRequest:
- return "Failure to create URL request."
- case .network:
- return "Network error."
+ case let .createURLRequest(error):
+ return "Failure to create URL request: \(error.localizedDescription)."
+ case let .network(error):
+ return "Network error: \(error.localizedDescription)."
case let .unhandledResponse(statusCode, serverResponse):
var str = "Failure to handle server response: HTTP/\(statusCode)."
@@ -41,8 +41,21 @@ extension REST {
}
return str
- case .decodeResponse:
- return "Failure to decode URL response data."
+ case let .decodeResponse(error):
+ return "Failure to decode URL response data: \(error.localizedDescription)."
+ }
+ }
+
+ var underlyingError: Swift.Error? {
+ switch self {
+ case let .network(error):
+ return error
+ case let .createURLRequest(error):
+ return error
+ case let .decodeResponse(error):
+ return error
+ case .unhandledResponse:
+ return nil
}
}
diff --git a/ios/MullvadVPN/REST/RESTNetworkOperation.swift b/ios/MullvadVPN/REST/RESTNetworkOperation.swift
index 02b1f5bff2..f1ffe472da 100644
--- a/ios/MullvadVPN/REST/RESTNetworkOperation.swift
+++ b/ios/MullvadVPN/REST/RESTNetworkOperation.swift
@@ -125,7 +125,7 @@ extension REST {
dispatchPrecondition(condition: .onQueue(dispatchQueue))
logger.error(
- chainedError: error,
+ error: error,
message: "Failed to request authorization."
)
@@ -165,7 +165,7 @@ extension REST {
dispatchPrecondition(condition: .onQueue(dispatchQueue))
logger.error(
- chainedError: error,
+ error: error,
message: "Failed to create URLRequest."
)
@@ -188,7 +188,7 @@ extension REST {
}
logger.error(
- chainedError: AnyChainedError(urlError),
+ error: urlError,
message: "Failed to perform request to \(endpoint)."
)
diff --git a/ios/MullvadVPN/RelayCache/RelayCacheTracker.swift b/ios/MullvadVPN/RelayCache/RelayCacheTracker.swift
index ec56e2b5bb..9698322559 100644
--- a/ios/MullvadVPN/RelayCache/RelayCacheTracker.swift
+++ b/ios/MullvadVPN/RelayCache/RelayCacheTracker.swift
@@ -104,7 +104,7 @@ extension RelayCache {
)
} catch {
logger.error(
- chainedError: AnyChainedError(error),
+ error: error,
message: "Failed to read the relay cache during initialization."
)
@@ -241,7 +241,7 @@ extension RelayCache {
if let error = mappedCompletion.error {
logger.error(
- chainedError: AnyChainedError(error),
+ error: error,
message: "Failed to update relays."
)
}
diff --git a/ios/MullvadVPN/SettingsManager/SettingsManager.swift b/ios/MullvadVPN/SettingsManager/SettingsManager.swift
index 95af7b2a99..cd40ef67d1 100644
--- a/ios/MullvadVPN/SettingsManager/SettingsManager.swift
+++ b/ios/MullvadVPN/SettingsManager/SettingsManager.swift
@@ -224,7 +224,7 @@ extension SettingsManager {
)
} catch {
logger.error(
- chainedError: AnyChainedError(error),
+ error: error,
message: "Failed to decode legacy settings."
)
return nil
@@ -248,7 +248,7 @@ extension SettingsManager {
if error != .itemNotFound {
logger.error(
- chainedError: AnyChainedError(error),
+ error: error,
message: "Failed to list legacy settings."
)
}
@@ -280,7 +280,7 @@ extension SettingsManager {
let error = KeychainError(code: status)
logger.error(
- chainedError: AnyChainedError(error),
+ error: error,
message: "Failed to remove legacy settings entry \(index)."
)
}
diff --git a/ios/MullvadVPN/ShortcutsManager.swift b/ios/MullvadVPN/ShortcutsManager.swift
index 4a6309429c..1baa525401 100644
--- a/ios/MullvadVPN/ShortcutsManager.swift
+++ b/ios/MullvadVPN/ShortcutsManager.swift
@@ -38,7 +38,7 @@ final class ShortcutsManager {
guard let self = self else { return }
if let error = error {
self.logger.error(
- chainedError: AnyChainedError(error),
+ error: error,
message: "Failed to fetch voice shortcuts."
)
return
diff --git a/ios/MullvadVPN/SimulatorTunnelProviderHost.swift b/ios/MullvadVPN/SimulatorTunnelProviderHost.swift
index 31d28ad84c..c30dda0e39 100644
--- a/ios/MullvadVPN/SimulatorTunnelProviderHost.swift
+++ b/ios/MullvadVPN/SimulatorTunnelProviderHost.swift
@@ -31,7 +31,7 @@ class SimulatorTunnelProviderHost: SimulatorTunnelProviderDelegate {
selectorResult = try tunnelOptions.getSelectorResult()
} catch {
self.providerLogger.error(
- chainedError: AnyChainedError(error),
+ error: error,
message: """
Failed to decode relay selector result passed from the app. \
Will continue by picking new relay.
@@ -45,7 +45,7 @@ class SimulatorTunnelProviderHost: SimulatorTunnelProviderDelegate {
completionHandler(nil)
} catch {
self.providerLogger.error(
- chainedError: AnyChainedError(error),
+ error: error,
message: "Failed to pick relay."
)
completionHandler(error)
@@ -72,7 +72,7 @@ class SimulatorTunnelProviderHost: SimulatorTunnelProviderDelegate {
completionHandler?(response)
} catch {
self.providerLogger.error(
- chainedError: AnyChainedError(error),
+ error: error,
message: "Failed to handle app message."
)
diff --git a/ios/MullvadVPN/TunnelManager/LoadTunnelConfigurationOperation.swift b/ios/MullvadVPN/TunnelManager/LoadTunnelConfigurationOperation.swift
index e98aaff5fd..70a3d95d25 100644
--- a/ios/MullvadVPN/TunnelManager/LoadTunnelConfigurationOperation.swift
+++ b/ios/MullvadVPN/TunnelManager/LoadTunnelConfigurationOperation.swift
@@ -51,7 +51,7 @@ class LoadTunnelConfigurationOperation: ResultOperation<Void, Error> {
tunnel.removeFromPreferences { error in
if let error = error {
self.logger.error(
- chainedError: AnyChainedError(error),
+ error: error,
message: "Failed to remove VPN configuration."
)
}
@@ -78,14 +78,14 @@ class LoadTunnelConfigurationOperation: ResultOperation<Void, Error> {
return .success(nil)
} else if let error = error as? DecodingError {
logger.error(
- chainedError: AnyChainedError(error),
+ error: error,
message: "Cannot decode settings. Will attempt to delete them from keychain."
)
return Result { try SettingsManager.deleteSettings() }
.mapError { error in
logger.error(
- chainedError: AnyChainedError(error),
+ error: error,
message: "Failed to delete settings from keychain."
)
@@ -109,14 +109,14 @@ class LoadTunnelConfigurationOperation: ResultOperation<Void, Error> {
return .success(nil)
} else if let error = error as? DecodingError {
logger.error(
- chainedError: AnyChainedError(error),
+ error: error,
message: "Cannot decode device state. Will attempt to delete it from keychain."
)
return Result { try SettingsManager.deleteDeviceState() }
.mapError { error in
logger.error(
- chainedError: AnyChainedError(error),
+ error: error,
message: "Failed to delete device state from keychain."
)
diff --git a/ios/MullvadVPN/TunnelManager/MigrateSettingsOperation.swift b/ios/MullvadVPN/TunnelManager/MigrateSettingsOperation.swift
index 17fc69cff1..adf3735f14 100644
--- a/ios/MullvadVPN/TunnelManager/MigrateSettingsOperation.swift
+++ b/ios/MullvadVPN/TunnelManager/MigrateSettingsOperation.swift
@@ -55,7 +55,7 @@ class MigrateSettingsOperation: AsyncOperation {
try SettingsManager.setLastUsedAccount(storedAccountNumber)
} catch {
logger.error(
- chainedError: AnyChainedError(error),
+ error: error,
message: "Failed to store last used account."
)
}
@@ -73,7 +73,7 @@ class MigrateSettingsOperation: AsyncOperation {
return
} catch {
logger.error(
- chainedError: AnyChainedError(error),
+ error: error,
message: "Failed to read legacy settings from keychain."
)
finishMigration()
@@ -147,7 +147,7 @@ class MigrateSettingsOperation: AsyncOperation {
self.accountData = accountData
case let .failure(error):
- logger.error(chainedError: error, message: "Failed to fetch accound data.")
+ logger.error(error: error, message: "Failed to fetch accound data.")
case .cancelled:
logger.debug("Account data request was cancelled.")
@@ -163,7 +163,7 @@ class MigrateSettingsOperation: AsyncOperation {
self.devices = devices
case let .failure(error):
- logger.error(chainedError: error, message: "Failed to fetch devices.")
+ logger.error(error: error, message: "Failed to fetch devices.")
case .cancelled:
logger.debug("Device request was cancelled.")
@@ -235,7 +235,7 @@ class MigrateSettingsOperation: AsyncOperation {
try SettingsManager.writeDeviceState(newDeviceState)
} catch {
logger.error(
- chainedError: AnyChainedError(error),
+ error: error,
message: "Failed to write migrated settings."
)
}
diff --git a/ios/MullvadVPN/TunnelManager/RotateKeyOperation.swift b/ios/MullvadVPN/TunnelManager/RotateKeyOperation.swift
index 5cdf10a872..0029373af7 100644
--- a/ios/MullvadVPN/TunnelManager/RotateKeyOperation.swift
+++ b/ios/MullvadVPN/TunnelManager/RotateKeyOperation.swift
@@ -107,7 +107,7 @@ class RotateKeyOperation: ResultOperation<Bool, Error> {
case let .failure(error):
logger.error(
- chainedError: AnyChainedError(error),
+ error: error,
message: "Failed to rotate device key."
)
finish(completion: .failure(error))
diff --git a/ios/MullvadVPN/TunnelManager/SendTunnelProviderMessageOperation.swift b/ios/MullvadVPN/TunnelManager/SendTunnelProviderMessageOperation.swift
index 2652433cee..9fb72e03d9 100644
--- a/ios/MullvadVPN/TunnelManager/SendTunnelProviderMessageOperation.swift
+++ b/ios/MullvadVPN/TunnelManager/SendTunnelProviderMessageOperation.swift
@@ -235,7 +235,7 @@ extension SendTunnelProviderMessageOperation where Output == Void {
}
}
-enum SendTunnelProviderMessageError: ChainedError {
+enum SendTunnelProviderMessageError: LocalizedError, WrappingError {
/// Tunnel process is either down or about to go down.
case tunnelDown(NEVPNStatus)
@@ -251,8 +251,17 @@ enum SendTunnelProviderMessageError: ChainedError {
return "Tunnel is either down or about to go down (status: \(status))."
case .timeout:
return "Send timeout."
- case .system:
- return "System error."
+ case let .system(error):
+ return "System error: \(error.localizedDescription)"
+ }
+ }
+
+ var underlyingError: Error? {
+ switch self {
+ case let .system(error):
+ return error
+ case .timeout, .tunnelDown:
+ return nil
}
}
}
diff --git a/ios/MullvadVPN/TunnelManager/SetAccountOperation.swift b/ios/MullvadVPN/TunnelManager/SetAccountOperation.swift
index 812869caaa..5ceb8cdfda 100644
--- a/ios/MullvadVPN/TunnelManager/SetAccountOperation.swift
+++ b/ios/MullvadVPN/TunnelManager/SetAccountOperation.swift
@@ -204,7 +204,7 @@ class SetAccountOperation: ResultOperation<StoredAccountData?, Error> {
let task = self.accountsProxy.createAccount(retryStrategy: .default) { completion in
let mappedCompletion = completion.mapError { error -> Error in
self.logger.error(
- chainedError: AnyChainedError(error),
+ error: error,
message: "Failed to create new account."
)
return error
@@ -243,7 +243,7 @@ class SetAccountOperation: ResultOperation<StoredAccountData?, Error> {
) { completion in
let mappedCompletion = completion.mapError { error -> Error in
self.logger.error(
- chainedError: AnyChainedError(error),
+ error: error,
message: "Failed to receive account data."
)
return error
@@ -286,7 +286,7 @@ class SetAccountOperation: ResultOperation<StoredAccountData?, Error> {
let mappedCompletion = completion
.mapError { error -> Error in
self.logger.error(
- chainedError: AnyChainedError(error),
+ error: error,
message: "Failed to delete device."
)
return error
@@ -333,7 +333,7 @@ class SetAccountOperation: ResultOperation<StoredAccountData?, Error> {
// Ignore error but log it.
if let error = error {
self.logger.error(
- chainedError: AnyChainedError(error),
+ error: error,
message: "Failed to remove VPN configuration."
)
}
@@ -362,7 +362,7 @@ class SetAccountOperation: ResultOperation<StoredAccountData?, Error> {
try SettingsManager.setLastUsedAccount(storedAccountData.number)
} catch {
self.logger.error(
- chainedError: AnyChainedError(error),
+ error: error,
message: "Failed to store last used account number."
)
}
@@ -386,7 +386,7 @@ class SetAccountOperation: ResultOperation<StoredAccountData?, Error> {
return (privateKey, device)
}
.mapError { error -> Error in
- self.logger.error(chainedError: error, message: "Failed to create device.")
+ self.logger.error(error: error, message: "Failed to create device.")
return error
}
diff --git a/ios/MullvadVPN/TunnelManager/StartTunnelOperation.swift b/ios/MullvadVPN/TunnelManager/StartTunnelOperation.swift
index f11164961e..f1eeee4a21 100644
--- a/ios/MullvadVPN/TunnelManager/StartTunnelOperation.swift
+++ b/ios/MullvadVPN/TunnelManager/StartTunnelOperation.swift
@@ -97,7 +97,7 @@ class StartTunnelOperation: ResultOperation<Void, Error> {
try tunnelOptions.setSelectorResult(selectorResult)
} catch {
logger.error(
- chainedError: AnyChainedError(error),
+ error: error,
message: "Failed to encode the selector result."
)
}
diff --git a/ios/MullvadVPN/TunnelManager/TunnelManager.swift b/ios/MullvadVPN/TunnelManager/TunnelManager.swift
index 1fa465b67b..b107491712 100644
--- a/ios/MullvadVPN/TunnelManager/TunnelManager.swift
+++ b/ios/MullvadVPN/TunnelManager/TunnelManager.swift
@@ -212,7 +212,7 @@ final class TunnelManager {
if case let .failure(error) = completion {
self.logger.error(
- chainedError: AnyChainedError(error),
+ error: error,
message: "Failed to load configuration."
)
}
@@ -261,7 +261,7 @@ final class TunnelManager {
DispatchQueue.main.async {
if let error = completion.error {
self.logger.error(
- chainedError: AnyChainedError(error),
+ error: error,
message: "Failed to start the tunnel."
)
@@ -293,7 +293,7 @@ final class TunnelManager {
DispatchQueue.main.async {
if let error = completion.error {
self.logger.error(
- chainedError: AnyChainedError(error),
+ error: error,
message: "Failed to stop the tunnel."
)
@@ -651,7 +651,7 @@ final class TunnelManager {
try SettingsManager.writeSettings(settings)
} catch {
logger.error(
- chainedError: AnyChainedError(error),
+ error: error,
message: "Failed to write settings."
)
}
@@ -679,7 +679,7 @@ final class TunnelManager {
try SettingsManager.writeDeviceState(deviceState)
} catch {
logger.error(
- chainedError: AnyChainedError(error),
+ error: error,
message: "Failed to write device state."
)
}
@@ -730,7 +730,7 @@ final class TunnelManager {
if let error = completion.error {
logger.error(
- chainedError: AnyChainedError(error),
+ error: error,
message: "Failed to reconnect the tunnel."
)
}
diff --git a/ios/MullvadVPN/TunnelManager/TunnelManagerErrors.swift b/ios/MullvadVPN/TunnelManager/TunnelManagerErrors.swift
index 8c25b173df..35b804a711 100644
--- a/ios/MullvadVPN/TunnelManager/TunnelManagerErrors.swift
+++ b/ios/MullvadVPN/TunnelManager/TunnelManagerErrors.swift
@@ -31,7 +31,9 @@ struct InvalidDeviceStateError: LocalizedError {
}
}
-struct StartTunnelError: LocalizedError {
+struct StartTunnelError: LocalizedError, WrappingError {
+ private let _underlyingError: Error
+
var errorDescription: String? {
return NSLocalizedString(
"START_TUNNEL_ERROR",
@@ -41,13 +43,18 @@ struct StartTunnelError: LocalizedError {
)
}
- let underlyingError: Error
+ var underlyingError: Error? {
+ return _underlyingError
+ }
+
init(underlyingError: Error) {
- self.underlyingError = underlyingError
+ _underlyingError = underlyingError
}
}
-struct StopTunnelError: LocalizedError {
+struct StopTunnelError: LocalizedError, WrappingError {
+ private let _underlyingError: Error
+
var errorDescription: String? {
return NSLocalizedString(
"STOP_TUNNEL_ERROR",
@@ -57,8 +64,11 @@ struct StopTunnelError: LocalizedError {
)
}
- let underlyingError: Error
+ var underlyingError: Error? {
+ return _underlyingError
+ }
+
init(underlyingError: Error) {
- self.underlyingError = underlyingError
+ _underlyingError = underlyingError
}
}
diff --git a/ios/MullvadVPN/TunnelManager/UpdateAccountDataOperation.swift b/ios/MullvadVPN/TunnelManager/UpdateAccountDataOperation.swift
index d0f62c68e4..b95d629ea5 100644
--- a/ios/MullvadVPN/TunnelManager/UpdateAccountDataOperation.swift
+++ b/ios/MullvadVPN/TunnelManager/UpdateAccountDataOperation.swift
@@ -52,7 +52,7 @@ class UpdateAccountDataOperation: ResultOperation<Void, Error> {
) {
let mappedCompletion = completion.mapError { error -> Error in
self.logger.error(
- chainedError: error,
+ error: error,
message: "Failed to fetch account expiry."
)
return error
diff --git a/ios/MullvadVPN/WrappingError.swift b/ios/MullvadVPN/WrappingError.swift
new file mode 100644
index 0000000000..ff217d0dae
--- /dev/null
+++ b/ios/MullvadVPN/WrappingError.swift
@@ -0,0 +1,14 @@
+//
+// WrappingError.swift
+// MullvadVPN
+//
+// Created by pronebird on 23/09/2022.
+// Copyright © 2022 Mullvad VPN AB. All rights reserved.
+//
+
+import Foundation
+
+/// Protocol describing errors that may contain underlying errors.
+protocol WrappingError: Error {
+ var underlyingError: Error? { get }
+}