Limits updating sources to app launch and manually via pull-to-refresh

This commit is contained in:
Riley Testut
2023-12-07 17:30:46 -06:00
committed by Magesh K
parent cf09843538
commit 9716ee6152
4 changed files with 158 additions and 163 deletions

View File

@@ -41,25 +41,21 @@ final class AppManagerPublisher: ObservableObject
fileprivate(set) var refreshProgress = [String: Progress]()
}
final class AppManager
class AppManager: ObservableObject
{
static let shared = AppManager()
private(set) var updatePatronsResult: Result<Void, Error>?
@Published
private(set) var updateSourcesResult: Result<Void, Error>? // nil == loading
private let operationQueue = OperationQueue()
private let serialOperationQueue = OperationQueue()
private var installationProgress = [String: Progress]() {
didSet {
self.publisher.installationProgress = self.installationProgress
}
}
private var refreshProgress = [String: Progress]() {
didSet {
self.publisher.refreshProgress = self.refreshProgress
}
}
@Published private var installationProgress = [String: Progress]()
@Published private var refreshProgress = [String: Progress]()
private var cancellables: Set<AnyCancellable> = []
private lazy var progressLock: UnsafeMutablePointer<os_unfair_lock> = {
// Can't safely pass &os_unfair_lock to os_unfair_lock functions in Swift,
@@ -70,9 +66,6 @@ final class AppManager
return lock
}()
private let publisher = AppManagerPublisher()
private var cancellables: Set<AnyCancellable> = []
private init()
{
self.operationQueue.name = "com.altstore.AppManager.operationQueue"
@@ -95,7 +88,7 @@ final class AppManager
/// Every time refreshProgress is changed, update all InstalledApps in memory
/// so that app.isRefreshing == refreshProgress.keys.contains(app.bundleID)
self.publisher.$refreshProgress
self.$refreshProgress
.receive(on: RunLoop.main)
.map(\.keys)
.flatMap { (bundleIDs) in
@@ -547,6 +540,68 @@ extension AppManager
self.run([updatePatronsOperation], context: nil)
}
func updateAllSources(completion: @escaping (Result<Void, Error>) -> Void)
{
self.updateSourcesResult = nil
self.fetchSources() { (result) in
do
{
do
{
let (_, context) = try result.get()
try context.save()
DispatchQueue.main.async {
self.updateSourcesResult = .success(())
completion(.success(()))
}
}
catch let error as AppManager.FetchSourcesError
{
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
{
Logger.main.error("Failed to assign error \(sanitizedError.localizedErrorCode) to source \(sourceID, privacy: .public). \(error.localizedDescription, privacy: .public)")
}
}
throw mergeError
}
}
catch var error as NSError
{
if error.localizedTitle == nil
{
error = error.withLocalizedTitle(NSLocalizedString("Unable to Refresh Store", comment: ""))
}
DispatchQueue.main.async {
self.updateSourcesResult = .failure(error)
completion(.failure(error))
}
}
}
}
}
extension AppManager
{
@discardableResult
func install<T: AppProtocol>(_ app: T, presentingViewController: UIViewController?, context: AuthenticatedOperationContext = AuthenticatedOperationContext(), completionHandler: @escaping (Result<InstalledApp, Error>) -> Void) -> RefreshGroup
{