mirror of
https://github.com/SideStore/SideStore.git
synced 2026-02-14 01:03:27 +01:00
Improves error handling when fetching multiple sources
Fetching sources is no longer all or nothing. Now if a source cannot be fetched, it won’t prevent other sources from being updated.
This commit is contained in:
@@ -197,15 +197,16 @@ extension AppManager
|
||||
self.run([fetchSourceOperation], context: nil)
|
||||
}
|
||||
|
||||
func fetchSources(completionHandler: @escaping (Result<Set<Source>, Error>) -> Void)
|
||||
func fetchSources(completionHandler: @escaping (Result<(Set<Source>, NSManagedObjectContext), FetchSourcesError>) -> Void)
|
||||
{
|
||||
DatabaseManager.shared.persistentContainer.performBackgroundTask { (context) in
|
||||
let sources = Source.all(in: context)
|
||||
guard !sources.isEmpty else { return completionHandler(.failure(OperationError.noSources)) }
|
||||
guard !sources.isEmpty else { return completionHandler(.failure(.init(OperationError.noSources))) }
|
||||
|
||||
let dispatchGroup = DispatchGroup()
|
||||
var fetchedSources = Set<Source>()
|
||||
var error: Error?
|
||||
|
||||
var errors = [Source: Error]()
|
||||
|
||||
let managedObjectContext = DatabaseManager.shared.persistentContainer.newBackgroundContext()
|
||||
|
||||
@@ -216,8 +217,10 @@ extension AppManager
|
||||
fetchSourceOperation.resultHandler = { (result) in
|
||||
switch result
|
||||
{
|
||||
case .failure(let e): error = e
|
||||
case .success(let source): fetchedSources.insert(source)
|
||||
case .failure(let error):
|
||||
let source = managedObjectContext.object(with: source.objectID) as! Source
|
||||
errors[source] = error
|
||||
}
|
||||
|
||||
dispatchGroup.leave()
|
||||
@@ -227,14 +230,15 @@ extension AppManager
|
||||
}
|
||||
|
||||
dispatchGroup.notify(queue: .global()) {
|
||||
if let error = error
|
||||
{
|
||||
completionHandler(.failure(error))
|
||||
}
|
||||
else
|
||||
{
|
||||
managedObjectContext.perform {
|
||||
completionHandler(.success(fetchedSources))
|
||||
managedObjectContext.perform {
|
||||
if !errors.isEmpty
|
||||
{
|
||||
let sources = Set(sources.compactMap { managedObjectContext.object(with: $0.objectID) as? Source })
|
||||
completionHandler(.failure(.init(sources: sources, errors: errors, context: managedObjectContext)))
|
||||
}
|
||||
else
|
||||
{
|
||||
completionHandler(.success((fetchedSources, managedObjectContext)))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
84
AltStore/Managing Apps/AppManagerErrors.swift
Normal file
84
AltStore/Managing Apps/AppManagerErrors.swift
Normal file
@@ -0,0 +1,84 @@
|
||||
//
|
||||
// AppManagerErrors.swift
|
||||
// AltStore
|
||||
//
|
||||
// Created by Riley Testut on 8/27/20.
|
||||
// Copyright © 2020 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CoreData
|
||||
|
||||
extension AppManager
|
||||
{
|
||||
struct FetchSourcesError: LocalizedError, CustomNSError
|
||||
{
|
||||
var primaryError: Error?
|
||||
|
||||
var sources: Set<Source>?
|
||||
var errors = [Source: Error]()
|
||||
|
||||
var managedObjectContext: NSManagedObjectContext?
|
||||
|
||||
var errorDescription: String? {
|
||||
if let error = self.primaryError
|
||||
{
|
||||
return error.localizedDescription
|
||||
}
|
||||
else
|
||||
{
|
||||
var localizedDescription: String?
|
||||
|
||||
self.managedObjectContext?.performAndWait {
|
||||
if self.sources?.count == 1
|
||||
{
|
||||
localizedDescription = NSLocalizedString("Could not refresh store.", comment: "")
|
||||
}
|
||||
else if self.errors.count == 1
|
||||
{
|
||||
guard let source = self.errors.keys.first else { return }
|
||||
localizedDescription = String(format: NSLocalizedString("Could not refresh source “%@”.", comment: ""), source.name)
|
||||
}
|
||||
else
|
||||
{
|
||||
localizedDescription = String(format: NSLocalizedString("Could not refresh %@ sources.", comment: ""), NSNumber(value: self.errors.count))
|
||||
}
|
||||
}
|
||||
|
||||
return localizedDescription
|
||||
}
|
||||
}
|
||||
|
||||
var recoverySuggestion: String? {
|
||||
if let error = self.primaryError as NSError?
|
||||
{
|
||||
return error.localizedRecoverySuggestion
|
||||
}
|
||||
else if self.errors.count == 1
|
||||
{
|
||||
return nil
|
||||
}
|
||||
else
|
||||
{
|
||||
return NSLocalizedString("Tap to view source errors.", comment: "")
|
||||
}
|
||||
}
|
||||
|
||||
var errorUserInfo: [String : Any] {
|
||||
guard let error = self.errors.values.first, self.errors.count == 1 else { return [:] }
|
||||
return [NSUnderlyingErrorKey: error]
|
||||
}
|
||||
|
||||
init(_ error: Error)
|
||||
{
|
||||
self.primaryError = error
|
||||
}
|
||||
|
||||
init(sources: Set<Source>, errors: [Source: Error], context: NSManagedObjectContext)
|
||||
{
|
||||
self.sources = sources
|
||||
self.errors = errors
|
||||
self.managedObjectContext = context
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user