From 779bde696cc3cd18d24b9ef603b18a9f42029309 Mon Sep 17 00:00:00 2001 From: Riley Testut Date: Tue, 16 Jul 2019 14:22:45 -0700 Subject: [PATCH] [AltStore] Improves app installation cancellation/progress tracking --- AltStore/Apps/AppManager.swift | 59 +++++++++++-------- AltStore/Operations/AppOperationContext.swift | 2 + AltStore/Operations/Operation.swift | 9 ++- 3 files changed, 46 insertions(+), 24 deletions(-) diff --git a/AltStore/Apps/AppManager.swift b/AltStore/Apps/AppManager.swift index 719de131..f252a842 100644 --- a/AltStore/Apps/AppManager.swift +++ b/AltStore/Apps/AppManager.swift @@ -26,6 +26,8 @@ class AppManager private let operationQueue = OperationQueue() private let processingQueue = DispatchQueue(label: "com.altstore.AppManager.processingQueue") + private var installationProgress = [App: Progress]() + private init() { self.operationQueue.name = "com.altstore.AppManager.operationQueue" @@ -105,10 +107,17 @@ extension AppManager { func install(_ app: App, presentingViewController: UIViewController, completionHandler: @escaping (Result) -> Void) -> Progress { + if let progress = self.installationProgress(for: app) + { + return progress + } + let group = self.install([app], forceDownload: true, presentingViewController: presentingViewController) - group.completionHandler = { (result) in + group.completionHandler = { (result) in do { + self.installationProgress[app] = nil + guard let (_, result) = try result.get().first else { throw OperationError.unknown } completionHandler(result) } @@ -118,6 +127,8 @@ extension AppManager } } + self.installationProgress[app] = group.progress + return group.progress } @@ -128,6 +139,12 @@ extension AppManager let group = self.install(apps, forceDownload: false, presentingViewController: presentingViewController, group: group) return group } + + func installationProgress(for app: App) -> Progress? + { + let progress = self.installationProgress[app] + return progress + } } private extension AppManager @@ -171,11 +188,8 @@ private extension AppManager /* Resign */ let resignAppOperation = ResignAppOperation(context: context) resignAppOperation.resultHandler = { (result) in - switch result - { - case .failure(let error): context.error = error - case .success(let fileURL): context.resignedFileURL = fileURL - } + guard let fileURL = self.process(result, context: context) else { return } + context.resignedFileURL = fileURL } resignAppOperation.addDependency(authenticationOperation) progress.addChild(resignAppOperation.progress, withPendingUnitCount: 20) @@ -203,11 +217,8 @@ private extension AppManager let downloadOperation = DownloadAppOperation(app: app) downloadOperation.resultHandler = { (result) in - switch result - { - case .failure(let error): context.error = error - case .success(let installedApp): context.installedApp = installedApp - } + guard let installedApp = self.process(result, context: context) else { return } + context.installedApp = installedApp } progress.addChild(downloadOperation.progress, withPendingUnitCount: 40) resignAppOperation.addDependency(downloadOperation) @@ -218,11 +229,8 @@ private extension AppManager /* Send */ let sendAppOperation = SendAppOperation(context: context) sendAppOperation.resultHandler = { (result) in - switch result - { - case .failure(let error): context.error = error - case .success(let connection): context.connection = connection - } + guard let connection = self.process(result, context: context) else { return } + context.connection = connection } progress.addChild(sendAppOperation.progress, withPendingUnitCount: 10) sendAppOperation.addDependency(resignAppOperation) @@ -232,12 +240,7 @@ private extension AppManager /* Install */ let installOperation = InstallAppOperation(context: context) installOperation.resultHandler = { (result) in - switch result - { - case .failure(let error): context.error = error - case .success: break - } - + guard let _ = self.process(result, context: context) else { return } self.finishAppOperation(context) } progress.addChild(installOperation.progress, withPendingUnitCount: 30) @@ -256,10 +259,17 @@ private extension AppManager @discardableResult func process(_ result: Result, context: AppOperationContext) -> T? { do - { + { let value = try result.get() return value } + catch OperationError.cancelled + { + context.error = OperationError.cancelled + self.finishAppOperation(context) + + return nil + } catch { context.error = error @@ -270,6 +280,9 @@ private extension AppManager func finishAppOperation(_ context: AppOperationContext) { self.processingQueue.sync { + guard !context.isFinished else { return } + context.isFinished = true + if let error = context.error { context.group.results[context.appIdentifier] = .failure(error) diff --git a/AltStore/Operations/AppOperationContext.swift b/AltStore/Operations/AppOperationContext.swift index 85d83070..6738a4b2 100644 --- a/AltStore/Operations/AppOperationContext.swift +++ b/AltStore/Operations/AppOperationContext.swift @@ -25,6 +25,8 @@ class AppOperationContext var resignedFileURL: URL? var connection: NWConnection? + var isFinished = false + var error: Error? { get { return _error ?? self.group.error diff --git a/AltStore/Operations/Operation.swift b/AltStore/Operations/Operation.swift index 1f648ef6..baba0384 100644 --- a/AltStore/Operations/Operation.swift +++ b/AltStore/Operations/Operation.swift @@ -23,7 +23,14 @@ class ResultOperation: Operation { guard !self.isFinished else { return } - self.resultHandler?(result) + if self.isCancelled + { + self.resultHandler?(.failure(OperationError.cancelled)) + } + else + { + self.resultHandler?(result) + } super.finish() }