Files
SideStore/Sources/Shared/Extensions/NSError+AltStore.swift
Joe Mattiello 8b1e87d2dd spm app builds
2023-03-10 19:30:17 -05:00

129 lines
5.1 KiB
Swift

//
// NSError+AltStore.swift
// AltStore
//
// Created by Riley Testut on 3/11/20.
// Copyright © 2020 Riley Testut. All rights reserved.
//
import Foundation
public extension NSError {
@objc(alt_localizedFailure)
var localizedFailure: String? {
let localizedFailure = (userInfo[NSLocalizedFailureErrorKey] as? String) ?? (NSError.userInfoValueProvider(forDomain: domain)?(self, NSLocalizedFailureErrorKey) as? String)
return localizedFailure
}
@objc(alt_localizedDebugDescription)
var localizedDebugDescription: String? {
let debugDescription = (userInfo[NSDebugDescriptionErrorKey] as? String) ?? (NSError.userInfoValueProvider(forDomain: domain)?(self, NSDebugDescriptionErrorKey) as? String)
return debugDescription
}
@objc(alt_errorWithLocalizedFailure:)
func withLocalizedFailure(_ failure: String) -> NSError {
var userInfo = self.userInfo
userInfo[NSLocalizedFailureErrorKey] = failure
if let failureReason = localizedFailureReason {
userInfo[NSLocalizedFailureReasonErrorKey] = failureReason
} else if localizedFailure == nil && localizedFailureReason == nil && localizedDescription.contains(localizedErrorCode) {
// Default localizedDescription, so replace with just the localized error code portion.
userInfo[NSLocalizedFailureReasonErrorKey] = "(\(localizedErrorCode).)"
} else {
userInfo[NSLocalizedFailureReasonErrorKey] = localizedDescription
}
if let localizedDescription = NSError.userInfoValueProvider(forDomain: domain)?(self, NSLocalizedDescriptionKey) as? String {
userInfo[NSLocalizedDescriptionKey] = localizedDescription
}
// Don't accidentally remove localizedDescription from dictionary
// userInfo[NSLocalizedDescriptionKey] = NSError.userInfoValueProvider(forDomain: self.domain)?(self, NSLocalizedDescriptionKey) as? String
if let recoverySuggestion = localizedRecoverySuggestion {
userInfo[NSLocalizedRecoverySuggestionErrorKey] = recoverySuggestion
}
let error = NSError(domain: domain, code: code, userInfo: userInfo)
return error
}
func sanitizedForCoreData() -> NSError {
var userInfo = self.userInfo
userInfo[NSLocalizedFailureErrorKey] = localizedFailure
userInfo[NSLocalizedDescriptionKey] = localizedDescription
userInfo[NSLocalizedFailureReasonErrorKey] = localizedFailureReason
userInfo[NSLocalizedRecoverySuggestionErrorKey] = localizedRecoverySuggestion
// Remove userInfo values that don't conform to NSSecureEncoding.
userInfo = userInfo.filter { _, value in
(value as AnyObject) is NSSecureCoding
}
// Sanitize underlying errors.
if let underlyingError = userInfo[NSUnderlyingErrorKey] as? Error {
let sanitizedError = (underlyingError as NSError).sanitizedForCoreData()
userInfo[NSUnderlyingErrorKey] = sanitizedError
}
if #available(iOS 14.5, macOS 11.3, *), let underlyingErrors = userInfo[NSMultipleUnderlyingErrorsKey] as? [Error] {
let sanitizedErrors = underlyingErrors.map { ($0 as NSError).sanitizedForCoreData() }
userInfo[NSMultipleUnderlyingErrorsKey] = sanitizedErrors
}
let error = NSError(domain: domain, code: code, userInfo: userInfo)
return error
}
}
public extension Error {
var underlyingError: Error? {
let underlyingError = (self as NSError).userInfo[NSUnderlyingErrorKey] as? Error
return underlyingError
}
var localizedErrorCode: String {
let localizedErrorCode = String(format: NSLocalizedString("%@ error %@", comment: ""), (self as NSError).domain, (self as NSError).code as NSNumber)
return localizedErrorCode
}
}
public protocol ALTLocalizedError: LocalizedError, CustomNSError {
var failure: String? { get }
var underlyingError: Error? { get }
}
public extension ALTLocalizedError {
var errorUserInfo: [String: Any] {
let userInfo = ([
NSLocalizedDescriptionKey: errorDescription,
NSLocalizedFailureReasonErrorKey: failureReason,
NSLocalizedFailureErrorKey: failure,
NSUnderlyingErrorKey: 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.
nil
}
var errorDescription: String? {
guard let errorFailure = failure else { return (underlyingError as NSError?)?.localizedDescription }
guard let failureReason = failureReason else { return errorFailure }
let errorDescription = errorFailure + " " + failureReason
return errorDescription
}
var failureReason: String? { (underlyingError as NSError?)?.localizedDescription }
var recoverySuggestion: String? { (underlyingError as NSError?)?.localizedRecoverySuggestion }
var helpAnchor: String? { (underlyingError as NSError?)?.helpAnchor }
}