mirror of
https://github.com/SideStore/SideStore.git
synced 2026-02-20 12:13:26 +01:00
[AltStore] Ensures apps are downloaded before attempting to refresh
This commit is contained in:
@@ -104,48 +104,17 @@ extension AppManager
|
|||||||
{
|
{
|
||||||
func install(_ app: App, presentingViewController: UIViewController, completionHandler: @escaping (Result<InstalledApp, Error>) -> Void) -> Progress
|
func install(_ app: App, presentingViewController: UIViewController, completionHandler: @escaping (Result<InstalledApp, Error>) -> Void) -> Progress
|
||||||
{
|
{
|
||||||
let progress = Progress.discreteProgress(totalUnitCount: 100)
|
let progress = self.install([app], forceDownload: true, presentingViewController: presentingViewController) { (result) in
|
||||||
|
do
|
||||||
// Authenticate
|
|
||||||
let authenticationOperation = AuthenticationOperation(presentingViewController: presentingViewController)
|
|
||||||
authenticationOperation.resultHandler = { (result) in
|
|
||||||
switch result
|
|
||||||
{
|
{
|
||||||
case .failure(let error): completionHandler(.failure(error))
|
guard let (_, result) = try result.get().first else { throw OperationError.unknown }
|
||||||
case .success(let signer):
|
completionHandler(result)
|
||||||
|
}
|
||||||
// Download
|
catch
|
||||||
app.managedObjectContext?.perform {
|
{
|
||||||
let downloadAppOperation = DownloadAppOperation(app: app)
|
completionHandler(.failure(error))
|
||||||
downloadAppOperation.resultHandler = { (result) in
|
|
||||||
switch result
|
|
||||||
{
|
|
||||||
case .failure(let error): completionHandler(.failure(error))
|
|
||||||
case .success(let installedApp):
|
|
||||||
let context = installedApp.managedObjectContext
|
|
||||||
|
|
||||||
// Refresh/Install
|
|
||||||
let (resignProgress, installProgress) = self.refresh(installedApp, signer: signer, presentingViewController: presentingViewController) { (result) in
|
|
||||||
switch result
|
|
||||||
{
|
|
||||||
case .failure(let error): completionHandler(.failure(error))
|
|
||||||
case .success:
|
|
||||||
context?.perform {
|
|
||||||
completionHandler(.success(installedApp))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
progress.addChild(resignProgress, withPendingUnitCount: 10)
|
|
||||||
progress.addChild(installProgress, withPendingUnitCount: 45)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
progress.addChild(downloadAppOperation.progress, withPendingUnitCount: 40)
|
|
||||||
self.operationQueue.addOperation(downloadAppOperation)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
progress.addChild(authenticationOperation.progress, withPendingUnitCount: 5)
|
|
||||||
self.operationQueue.addOperation(authenticationOperation)
|
|
||||||
|
|
||||||
return progress
|
return progress
|
||||||
}
|
}
|
||||||
@@ -167,9 +136,20 @@ extension AppManager
|
|||||||
|
|
||||||
@discardableResult func refresh(_ installedApps: [InstalledApp], presentingViewController: UIViewController?, completionHandler: @escaping (Result<[String: Result<InstalledApp, Error>], Error>) -> Void) -> Progress
|
@discardableResult func refresh(_ installedApps: [InstalledApp], presentingViewController: UIViewController?, completionHandler: @escaping (Result<[String: Result<InstalledApp, Error>], Error>) -> Void) -> Progress
|
||||||
{
|
{
|
||||||
let progress = Progress.discreteProgress(totalUnitCount: Int64(installedApps.count))
|
let apps = installedApps.compactMap { $0.app }
|
||||||
|
|
||||||
guard let context = installedApps.first?.managedObjectContext else {
|
let progress = self.install(apps, forceDownload: false, presentingViewController: presentingViewController, completionHandler: completionHandler)
|
||||||
|
return progress
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension AppManager
|
||||||
|
{
|
||||||
|
func install(_ apps: [App], forceDownload: Bool, presentingViewController: UIViewController?, completionHandler: @escaping (Result<[String: Result<InstalledApp, Error>], Error>) -> Void) -> Progress
|
||||||
|
{
|
||||||
|
let progress = Progress.discreteProgress(totalUnitCount: Int64(apps.count))
|
||||||
|
|
||||||
|
guard let context = apps.first?.managedObjectContext else {
|
||||||
completionHandler(.success([:]))
|
completionHandler(.success([:]))
|
||||||
return progress
|
return progress
|
||||||
}
|
}
|
||||||
@@ -182,33 +162,76 @@ extension AppManager
|
|||||||
case .failure(let error): completionHandler(.failure(error))
|
case .failure(let error): completionHandler(.failure(error))
|
||||||
case .success(let signer):
|
case .success(let signer):
|
||||||
|
|
||||||
// Refresh
|
// Download
|
||||||
context.perform {
|
context.perform {
|
||||||
let dispatchGroup = DispatchGroup()
|
let dispatchGroup = DispatchGroup()
|
||||||
var results = [String: Result<InstalledApp, Error>]()
|
var results = [String: Result<InstalledApp, Error>]()
|
||||||
|
|
||||||
for installedApp in installedApps
|
let backgroundContext = DatabaseManager.shared.persistentContainer.newBackgroundContext()
|
||||||
|
|
||||||
|
for app in apps
|
||||||
{
|
{
|
||||||
let bundleIdentifier = installedApp.bundleIdentifier
|
let appProgress = Progress(totalUnitCount: 100)
|
||||||
print("Refreshing App:", bundleIdentifier)
|
|
||||||
|
let appID = app.identifier
|
||||||
|
print("Installing app:", appID)
|
||||||
|
|
||||||
dispatchGroup.enter()
|
dispatchGroup.enter()
|
||||||
|
|
||||||
let (resignProgress, installProgress) = self.refresh(installedApp, signer: signer, presentingViewController: presentingViewController) { (result) in
|
func finishApp(_ result: Result<InstalledApp, Error>)
|
||||||
print("Refreshed App: \(bundleIdentifier).", result)
|
{
|
||||||
results[bundleIdentifier] = result
|
switch result
|
||||||
|
{
|
||||||
|
case .failure(let error): print("Failed to install app \(appID).", error)
|
||||||
|
case .success: print("Installed app:", appID)
|
||||||
|
}
|
||||||
|
|
||||||
|
results[appID] = result
|
||||||
dispatchGroup.leave()
|
dispatchGroup.leave()
|
||||||
}
|
}
|
||||||
|
|
||||||
let refreshProgress = Progress(totalUnitCount: 100)
|
// Ensure app is downloaded.
|
||||||
refreshProgress.addChild(resignProgress, withPendingUnitCount: 20)
|
let downloadAppOperation = DownloadAppOperation(app: app)
|
||||||
refreshProgress.addChild(installProgress, withPendingUnitCount: 80)
|
downloadAppOperation.useCachedAppIfAvailable = !forceDownload
|
||||||
|
downloadAppOperation.context = backgroundContext
|
||||||
|
downloadAppOperation.resultHandler = { (result) in
|
||||||
|
switch result
|
||||||
|
{
|
||||||
|
case .failure(let error):
|
||||||
|
finishApp(.failure(error))
|
||||||
|
|
||||||
progress.addChild(refreshProgress, withPendingUnitCount: 1)
|
case .success(let installedApp):
|
||||||
|
|
||||||
|
// Refresh
|
||||||
|
let (resignProgress, installProgress) = self.refresh(installedApp, signer: signer, presentingViewController: presentingViewController) { (result) in
|
||||||
|
finishApp(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
if forceDownload
|
||||||
|
{
|
||||||
|
appProgress.addChild(resignProgress, withPendingUnitCount: 10)
|
||||||
|
appProgress.addChild(installProgress, withPendingUnitCount: 50)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
appProgress.addChild(resignProgress, withPendingUnitCount: 20)
|
||||||
|
appProgress.addChild(installProgress, withPendingUnitCount: 80)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if forceDownload
|
||||||
|
{
|
||||||
|
appProgress.addChild(downloadAppOperation.progress, withPendingUnitCount: 40)
|
||||||
|
}
|
||||||
|
|
||||||
|
progress.addChild(appProgress, withPendingUnitCount: 1)
|
||||||
|
|
||||||
|
self.operationQueue.addOperation(downloadAppOperation)
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatchGroup.notify(queue: .global()) {
|
dispatchGroup.notify(queue: .global()) {
|
||||||
context.perform {
|
backgroundContext.perform {
|
||||||
completionHandler(.success(results))
|
completionHandler(.success(results))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -220,10 +243,7 @@ extension AppManager
|
|||||||
|
|
||||||
return progress
|
return progress
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private extension AppManager
|
|
||||||
{
|
|
||||||
func refresh(_ installedApp: InstalledApp, signer: ALTSigner, presentingViewController: UIViewController?, completionHandler: @escaping (Result<InstalledApp, Error>) -> Void) -> (Progress, Progress)
|
func refresh(_ installedApp: InstalledApp, signer: ALTSigner, presentingViewController: UIViewController?, completionHandler: @escaping (Result<InstalledApp, Error>) -> Void) -> (Progress, Progress)
|
||||||
{
|
{
|
||||||
let context = installedApp.managedObjectContext
|
let context = installedApp.managedObjectContext
|
||||||
|
|||||||
@@ -15,6 +15,10 @@ import AltSign
|
|||||||
class DownloadAppOperation: ResultOperation<InstalledApp>
|
class DownloadAppOperation: ResultOperation<InstalledApp>
|
||||||
{
|
{
|
||||||
let app: App
|
let app: App
|
||||||
|
|
||||||
|
var useCachedAppIfAvailable = false
|
||||||
|
lazy var context = DatabaseManager.shared.persistentContainer.newBackgroundContext()
|
||||||
|
|
||||||
private let downloadURL: URL
|
private let downloadURL: URL
|
||||||
private let ipaURL: URL
|
private let ipaURL: URL
|
||||||
|
|
||||||
@@ -35,15 +39,16 @@ class DownloadAppOperation: ResultOperation<InstalledApp>
|
|||||||
{
|
{
|
||||||
super.main()
|
super.main()
|
||||||
|
|
||||||
let downloadTask = self.session.downloadTask(with: self.downloadURL) { (fileURL, response, error) in
|
func finish(error: Error?)
|
||||||
do
|
{
|
||||||
|
if let error = error
|
||||||
{
|
{
|
||||||
let (fileURL, _) = try Result((fileURL, response), error).get()
|
self.finish(.failure(error))
|
||||||
|
}
|
||||||
try FileManager.default.copyItem(at: fileURL, to: self.ipaURL, shouldReplace: true)
|
else
|
||||||
|
{
|
||||||
DatabaseManager.shared.persistentContainer.performBackgroundTask { (context) in
|
self.context.perform {
|
||||||
let app = context.object(with: self.app.objectID) as! App
|
let app = self.context.object(with: self.app.objectID) as! App
|
||||||
|
|
||||||
let installedApp: InstalledApp
|
let installedApp: InstalledApp
|
||||||
|
|
||||||
@@ -57,16 +62,33 @@ class DownloadAppOperation: ResultOperation<InstalledApp>
|
|||||||
installedApp = InstalledApp(app: app,
|
installedApp = InstalledApp(app: app,
|
||||||
bundleIdentifier: app.identifier,
|
bundleIdentifier: app.identifier,
|
||||||
expirationDate: Date(),
|
expirationDate: Date(),
|
||||||
context: context)
|
context: self.context)
|
||||||
}
|
}
|
||||||
|
|
||||||
installedApp.version = app.version
|
installedApp.version = app.version
|
||||||
self.finish(.success(installedApp))
|
self.finish(.success(installedApp))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.useCachedAppIfAvailable && FileManager.default.fileExists(atPath: self.ipaURL.path)
|
||||||
|
{
|
||||||
|
finish(error: nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let downloadTask = self.session.downloadTask(with: self.downloadURL) { (fileURL, response, error) in
|
||||||
|
do
|
||||||
|
{
|
||||||
|
let (fileURL, _) = try Result((fileURL, response), error).get()
|
||||||
|
|
||||||
|
try FileManager.default.copyItem(at: fileURL, to: self.ipaURL, shouldReplace: true)
|
||||||
|
|
||||||
|
finish(error: nil)
|
||||||
|
}
|
||||||
catch let error
|
catch let error
|
||||||
{
|
{
|
||||||
self.finish(.failure(error))
|
finish(error: error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user