From 6e4feecff0a1b951dc0b53b17c251b2e7d00cee5 Mon Sep 17 00:00:00 2001 From: Riley Testut Date: Fri, 26 Feb 2021 13:50:50 -0600 Subject: [PATCH] Adds ALTLocalizedError.underlyingError Allows for easily wrapping underlying errors while preserving localized descriptions. --- AltStore/Components/ToastView.swift | 4 +-- AltStore/Extensions/NSError+AltStore.swift | 38 ++++++++++++++++++++-- 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/AltStore/Components/ToastView.swift b/AltStore/Components/ToastView.swift index ec6ecdb5..b58c5dfa 100644 --- a/AltStore/Components/ToastView.swift +++ b/AltStore/Components/ToastView.swift @@ -49,7 +49,7 @@ class ToastView: RSTToastView convenience init(error: Error) { var error = error as NSError - var underlyingError = error.userInfo[NSUnderlyingErrorKey] as? NSError + var underlyingError = error.underlyingError var preferredDuration: TimeInterval? @@ -59,7 +59,7 @@ class ToastView: RSTToastView { // Treat underlyingError as the primary error. - error = unwrappedUnderlyingError + error = unwrappedUnderlyingError as NSError underlyingError = nil preferredDuration = .longToastViewDuration diff --git a/AltStore/Extensions/NSError+AltStore.swift b/AltStore/Extensions/NSError+AltStore.swift index ccb01b7e..90b9346d 100644 --- a/AltStore/Extensions/NSError+AltStore.swift +++ b/AltStore/Extensions/NSError+AltStore.swift @@ -44,17 +44,49 @@ extension NSError } } +extension Error +{ + var underlyingError: Error? { + let underlyingError = (self as NSError).userInfo[NSUnderlyingErrorKey] as? Error + return underlyingError + } +} + protocol ALTLocalizedError: LocalizedError, CustomNSError { var failure: String? { get } + + var underlyingError: Error? { get } } extension ALTLocalizedError { var errorUserInfo: [String : Any] { - let userInfo = [NSLocalizedDescriptionKey: self.errorDescription, - NSLocalizedFailureReasonErrorKey: self.failureReason, - NSLocalizedFailureErrorKey: self.failure].compactMapValues { $0 } + let userInfo = ([ + NSLocalizedDescriptionKey: self.errorDescription, + NSLocalizedFailureReasonErrorKey: self.failureReason, + NSLocalizedFailureErrorKey: self.failure, + NSUnderlyingErrorKey: self.underlyingError + ] as [String: Any?]).compactMapValues { $0 } return userInfo } + + var underlyingError: Error? { + // Error's default implementation calls errorUserInfo, + // but ALTLocalizedError.errorUserInfo calls underlyingError. + // Return nil to prevent infinite recursion. + return nil + } + + var errorDescription: String? { + guard let errorFailure = self.failure else { return (self.underlyingError as NSError?)?.localizedDescription } + guard let failureReason = self.failureReason else { return errorFailure } + + let errorDescription = errorFailure + " " + failureReason + return errorDescription + } + + var failureReason: String? { (self.underlyingError as NSError?)?.localizedFailureReason ?? (self.underlyingError as NSError?)?.localizedDescription } + var recoverySuggestion: String? { (self.underlyingError as NSError?)?.localizedRecoverySuggestion } + var helpAnchor: String? { (self.underlyingError as NSError?)?.helpAnchor } }