From a8f0d9da9baab3ec95abe654779931a9d1bbb920 Mon Sep 17 00:00:00 2001 From: Riley Testut Date: Fri, 13 Jan 2023 14:46:42 -0600 Subject: [PATCH] Caches MergeErrors when refreshing sources to view later in SourcesViewController --- AltStore/Browse/BrowseViewController.swift | 21 ++++++++ AltStore/My Apps/MyAppsViewController.swift | 21 ++++++++ AltStore/News/NewsViewController.swift | 21 ++++++++ AltStoreCore/Model/MergePolicy.swift | 58 ++++++++++++++++----- 4 files changed, 109 insertions(+), 12 deletions(-) diff --git a/AltStore/Browse/BrowseViewController.swift b/AltStore/Browse/BrowseViewController.swift index b308a9ee..6693fa3c 100644 --- a/AltStore/Browse/BrowseViewController.swift +++ b/AltStore/Browse/BrowseViewController.swift @@ -192,6 +192,27 @@ private extension BrowseViewController try error.managedObjectContext?.save() throw error } + catch let mergeError as MergeError + { + guard let sourceID = mergeError.sourceID else { throw mergeError } + + let sanitizedError = (mergeError as NSError).sanitizedForSerialization() + DatabaseManager.shared.persistentContainer.performBackgroundTask { context in + do + { + guard let source = Source.first(satisfying: NSPredicate(format: "%K == %@", #keyPath(Source.identifier), sourceID), in: context) else { return } + + source.error = sanitizedError + try context.save() + } + catch + { + print("[ALTLog] Failed to assign error \(sanitizedError.localizedErrorCode) to source \(sourceID).", error) + } + } + + throw mergeError + } } catch var error as NSError { diff --git a/AltStore/My Apps/MyAppsViewController.swift b/AltStore/My Apps/MyAppsViewController.swift index ed5522be..9860cfd3 100644 --- a/AltStore/My Apps/MyAppsViewController.swift +++ b/AltStore/My Apps/MyAppsViewController.swift @@ -1480,6 +1480,27 @@ private extension MyAppsViewController try error.managedObjectContext?.save() throw error } + catch let mergeError as MergeError + { + guard let sourceID = mergeError.sourceID else { throw mergeError } + + let sanitizedError = (mergeError as NSError).sanitizedForSerialization() + DatabaseManager.shared.persistentContainer.performBackgroundTask { context in + do + { + guard let source = Source.first(satisfying: NSPredicate(format: "%K == %@", #keyPath(Source.identifier), sourceID), in: context) else { return } + + source.error = sanitizedError + try context.save() + } + catch + { + print("[ALTLog] Failed to assign error \(sanitizedError.localizedErrorCode) to source \(sourceID).", error) + } + } + + throw mergeError + } } catch let error as NSError { diff --git a/AltStore/News/NewsViewController.swift b/AltStore/News/NewsViewController.swift index 82af2d8c..9981d2a0 100644 --- a/AltStore/News/NewsViewController.swift +++ b/AltStore/News/NewsViewController.swift @@ -209,6 +209,27 @@ private extension NewsViewController try error.managedObjectContext?.save() throw error } + catch let mergeError as MergeError + { + guard let sourceID = mergeError.sourceID else { throw mergeError } + + let sanitizedError = (mergeError as NSError).sanitizedForSerialization() + DatabaseManager.shared.persistentContainer.performBackgroundTask { context in + do + { + guard let source = Source.first(satisfying: NSPredicate(format: "%K == %@", #keyPath(Source.identifier), sourceID), in: context) else { return } + + source.error = sanitizedError + try context.save() + } + catch + { + print("[ALTLog] Failed to assign error \(sanitizedError.localizedErrorCode) to source \(sourceID).", error) + } + } + + throw mergeError + } } catch var error as NSError { diff --git a/AltStoreCore/Model/MergePolicy.swift b/AltStoreCore/Model/MergePolicy.swift index 35f871c8..f52e02ee 100644 --- a/AltStoreCore/Model/MergePolicy.swift +++ b/AltStoreCore/Model/MergePolicy.swift @@ -8,13 +8,14 @@ import CoreData +import AltSign import Roxas extension MergeError { - enum Code: Int, ALTErrorCode + public enum Code: Int, ALTErrorCode { - typealias Error = MergeError + public typealias Error = MergeError case noVersions case incorrectVersionOrder @@ -24,19 +25,19 @@ extension MergeError static func incorrectVersionOrder(for app: StoreApp) -> MergeError { .init(code: .incorrectVersionOrder, appName: app.name, appBundleID: app.bundleIdentifier, sourceID: app.sourceIdentifier) } } -struct MergeError: ALTLocalizedError +public struct MergeError: ALTLocalizedError { - static var errorDomain: String { "AltStore.MergeError" } + public static var errorDomain: String { "AltStore.MergeError" } - let code: Code - var errorTitle: String? - var errorFailure: String? + public let code: Code + public var errorTitle: String? + public var errorFailure: String? - var appName: String? - var appBundleID: String? - var sourceID: String? + public var appName: String? + public var appBundleID: String? + public var sourceID: String? - var errorFailureReason: String { + public var errorFailureReason: String { switch self.code { case .noVersions: @@ -59,7 +60,7 @@ struct MergeError: ALTLocalizedError } } - var recoverySuggestion: String? { + public var recoverySuggestion: String? { switch self.code { case .incorrectVersionOrder: return NSLocalizedString("Please try again later.", comment: "") @@ -68,6 +69,39 @@ struct MergeError: ALTLocalizedError } } +// Necessary to cast back to MergeError from NSError when thrown from NSMergePolicy. +extension MergeError: _ObjectiveCBridgeableError +{ + public var errorUserInfo: [String : Any] { + // Copied from ALTLocalizedError + var userInfo: [String: Any?] = [ + NSLocalizedFailureErrorKey: self.errorFailure, + ALTLocalizedTitleErrorKey: self.errorTitle, + ALTSourceFileErrorKey: self.sourceFile, + ALTSourceLineErrorKey: self.sourceLine, + ] + + userInfo["appName"] = self.appName + userInfo["appBundleID"] = self.appBundleID + userInfo["sourceID"] = self.sourceID + + return userInfo.compactMapValues { $0 } + } + + public init?(_bridgedNSError error: NSError) + { + guard error.domain == MergeError.errorDomain, let code = Code(rawValue: error.code) else { return nil } + + self.code = code + self.errorTitle = error.localizedTitle + self.errorFailure = error.localizedFailure + + self.appName = error.userInfo["appName"] as? String + self.appBundleID = error.userInfo["appBundleID"] as? String + self.sourceID = error.userInfo["sourceID"] as? String + } +} + private extension Error { func serialized(withFailure failure: String) -> NSError