From 205fb3d7e9a725f9c1a3692b01b9f266cf103767 Mon Sep 17 00:00:00 2001 From: Riley Testut Date: Mon, 17 Jun 2019 16:31:10 -0700 Subject: [PATCH] [AltStore] Update AltStore itself from UpdatesViewController --- AltStore/AppDelegate.swift | 4 +- AltStore/Apps/AppsViewController.swift | 1 + AltStore/Info.plist | 14 ++++ AltStore/Model/App.swift | 25 ++++++- AltStore/Model/DatabaseManager.swift | 54 +++++++++++++- AltStore/Resources/Apps.json | 11 +++ AltStore/Updates/UpdatesViewController.swift | 78 ++++++++++++-------- 7 files changed, 151 insertions(+), 36 deletions(-) diff --git a/AltStore/AppDelegate.swift b/AltStore/AppDelegate.swift index 4a03c0be..7201abf2 100644 --- a/AltStore/AppDelegate.swift +++ b/AltStore/AppDelegate.swift @@ -30,7 +30,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate { { print("Started DatabaseManager") - AppManager.shared.update() + DispatchQueue.main.async { + AppManager.shared.update() + } } } diff --git a/AltStore/Apps/AppsViewController.swift b/AltStore/Apps/AppsViewController.swift index 59bd5700..083ee7f0 100644 --- a/AltStore/Apps/AppsViewController.swift +++ b/AltStore/Apps/AppsViewController.swift @@ -50,6 +50,7 @@ private extension AppsViewController let fetchRequest = App.fetchRequest() as NSFetchRequest fetchRequest.relationshipKeyPathsForPrefetching = [#keyPath(InstalledApp.app)] fetchRequest.sortDescriptors = [NSSortDescriptor(keyPath: \App.name, ascending: true)] + fetchRequest.predicate = NSPredicate(format: "%K != %@", #keyPath(App.identifier), App.altstoreAppID) fetchRequest.returnsObjectsAsFaults = false let dataSource = RSTFetchedResultsTableViewDataSource(fetchRequest: fetchRequest, managedObjectContext: DatabaseManager.shared.viewContext) diff --git a/AltStore/Info.plist b/AltStore/Info.plist index 4d6f21c8..237c4e22 100644 --- a/AltStore/Info.plist +++ b/AltStore/Info.plist @@ -18,10 +18,24 @@ APPL CFBundleShortVersionString 1.0 + CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLName + AltStore + CFBundleURLSchemes + + altstore-com.rileytestut.altstore + + + CFBundleVersion 1 LSApplicationQueriesSchemes + altstore-com.rileytestut.altstore altstore-com.rileytestut.Delta altstore-com.rileytestut.ClipboardManager diff --git a/AltStore/Model/App.swift b/AltStore/Model/App.swift index b3f1bfc7..898c98af 100644 --- a/AltStore/Model/App.swift +++ b/AltStore/Model/App.swift @@ -11,8 +11,13 @@ import CoreData import Roxas +extension App +{ + static let altstoreAppID = "com.rileytestut.AltStore" +} + @objc(App) -class App: NSManagedObject, Decodable +class App: NSManagedObject, Decodable, Fetchable { /* Properties */ @NSManaged private(set) var name: String @@ -24,7 +29,7 @@ class App: NSManagedObject, Decodable @NSManaged private(set) var iconName: String @NSManaged private(set) var screenshotNames: [String] - @NSManaged private(set) var version: String + @NSManaged var version: String @NSManaged private(set) var versionDate: Date @NSManaged private(set) var versionDescription: String? @@ -83,4 +88,20 @@ extension App { return NSFetchRequest(entityName: "App") } + + class func makeAltStoreApp(in context: NSManagedObjectContext) -> App + { + let app = App(context: context) + app.name = "AltStore" + app.identifier = "com.rileytestut.AltStore" + app.developerName = "Riley Testut" + app.localizedDescription = "AltStore is an alternative App Store." + app.iconName = "" + app.screenshotNames = [] + app.version = "1.0" + app.versionDate = Date() + app.downloadURL = URL(string: "http://rileytestut.com")! + + return app + } } diff --git a/AltStore/Model/DatabaseManager.swift b/AltStore/Model/DatabaseManager.swift index 7374b25a..64926fff 100644 --- a/AltStore/Model/DatabaseManager.swift +++ b/AltStore/Model/DatabaseManager.swift @@ -33,9 +33,15 @@ public extension DatabaseManager self.persistentContainer.loadPersistentStores { (description, error) in guard error == nil else { return completionHandler(error!) } - self.isStarted = true - - completionHandler(error) + self.prepareDatabase() { (result) in + switch result + { + case .failure(let error): completionHandler(error) + case .success: + self.isStarted = true + completionHandler(nil) + } + } } } @@ -94,3 +100,45 @@ extension DatabaseManager return activeTeam } } + +private extension DatabaseManager +{ + func prepareDatabase(completionHandler: @escaping (Result) -> Void) + { + self.persistentContainer.performBackgroundTask { (context) in + let version = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String ?? "1.0" + + let altStoreApp: App + + if let app = App.first(satisfying: NSPredicate(format: "%K == %@", #keyPath(App.identifier), App.altstoreAppID), in: context) + { + altStoreApp = app + } + else + { + altStoreApp = App.makeAltStoreApp(in: context) + altStoreApp.version = version + } + + if let installedApp = altStoreApp.installedApp + { + installedApp.version = version + } + else + { + let installedApp = InstalledApp(app: altStoreApp, bundleIdentifier: altStoreApp.identifier, expirationDate: Date(), context: context) + installedApp.version = version + } + + do + { + try context.save() + completionHandler(.success(())) + } + catch + { + completionHandler(.failure(error)) + } + } + } +} diff --git a/AltStore/Resources/Apps.json b/AltStore/Resources/Apps.json index 38573eb8..40be9899 100644 --- a/AltStore/Resources/Apps.json +++ b/AltStore/Resources/Apps.json @@ -1,4 +1,15 @@ [ + { + "name": "AltStore", + "identifier": "com.rileytestut.AltStore", + "developerName": "Riley Testut", + "version": "1.0", + "versionDate": "2019-05-20", + "versionDescription": "AltStore has been updated with bug fixes and improvements.", + "downloadURL": "https://www.dropbox.com/s/w1gn9iztlqvltyp/AltStore.ipa?dl=1", + "localizedDescription": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Sed enim ut sem viverra aliquet eget sit amet. Viverra mauris in aliquam sem fringilla ut. Egestas erat imperdiet sed euismod nisi porta. Sit amet dictum sit amet justo donec enim diam vulputate. Phasellus vestibulum lorem sed risus ultricies tristique nulla. Elit pellentesque habitant morbi tristique. Ut lectus arcu bibendum at. Ullamcorper a lacus vestibulum sed. Mi tempus imperdiet nulla malesuada pellentesque elit eget gravida. Nec feugiat nisl pretium fusce id velit ut. Amet nulla facilisi morbi tempus. Ut sem nulla pharetra diam sit amet nisl.\n\nTortor at auctor urna nunc id cursus metus. Commodo ullamcorper a lacus vestibulum sed arcu non odio. Faucibus turpis in eu mi bibendum neque egestas. Auctor augue mauris augue neque gravida in fermentum et sollicitudin. Aliquam vestibulum morbi blandit cursus risus at ultrices mi tempus. Placerat in egestas erat imperdiet sed euismod nisi. Aliquam id diam maecenas ultricies mi eget mauris. Nunc faucibus a pellentesque sit amet porttitor eget dolor. Sed faucibus turpis in eu mi. Tortor vitae purus faucibus ornare suspendisse sed nisi lacus sed. Id semper risus in hendrerit gravida rutrum quisque. At lectus urna duis convallis convallis. Egestas maecenas pharetra convallis posuere. Id velit ut tortor pretium viverra. Quam pellentesque nec nam aliquam sem et tortor consequat. Risus pretium quam vulputate dignissim. Urna nec tincidunt praesent semper feugiat nibh sed pulvinar.\n\nTempor orci eu lobortis elementum nibh tellus. Mattis rhoncus urna neque viverra justo nec. Maecenas pharetra convallis posuere morbi leo. Rhoncus mattis rhoncus urna neque viverra justo nec. Gravida dictum fusce ut placerat orci nulla pellentesque dignissim enim. At imperdiet dui accumsan sit amet. Elit sed vulputate mi sit amet mauris commodo. Pellentesque habitant morbi tristique senectus. Tortor id aliquet lectus proin nibh. Magna etiam tempor orci eu lobortis elementum. Est pellentesque elit ullamcorper dignissim. Dapibus ultrices in iaculis nunc. Commodo quis imperdiet massa tincidunt nunc pulvinar sapien et ligula. Diam vel quam elementum pulvinar. Vel turpis nunc eget lorem dolor sed viverra. Tortor pretium viverra suspendisse potenti nullam ac tortor vitae. Euismod nisi porta lorem mollis. Massa id neque aliquam vestibulum morbi blandit.\n\nMassa massa ultricies mi quis hendrerit dolor magna eget est. Augue interdum velit euismod in pellentesque massa. Sed risus ultricies tristique nulla aliquet enim. Risus viverra adipiscing at in tellus. Donec adipiscing tristique risus nec feugiat. Eget sit amet tellus cras adipiscing enim eu turpis. Auctor neque vitae tempus quam pellentesque nec. Sit amet tellus cras adipiscing enim eu turpis egestas. Dui faucibus in ornare quam viverra. Fermentum iaculis eu non diam phasellus vestibulum lorem. Odio ut enim blandit volutpat maecenas. Dolor sit amet consectetur adipiscing elit pellentesque habitant morbi tristique. Pellentesque diam volutpat commodo sed egestas egestas. Aliquam purus sit amet luctus venenatis lectus magna fringilla. Viverra mauris in aliquam sem fringilla ut morbi tincidunt. Elit duis tristique sollicitudin nibh sit. Fermentum dui faucibus in ornare quam viverra orci sagittis. Aliquet eget sit amet tellus cras adipiscing.", + "iconName": "DeltaIcon" + }, { "name": "Delta", "identifier": "com.rileytestut.Delta", diff --git a/AltStore/Updates/UpdatesViewController.swift b/AltStore/Updates/UpdatesViewController.swift index 6a8d2011..960f185d 100644 --- a/AltStore/Updates/UpdatesViewController.swift +++ b/AltStore/Updates/UpdatesViewController.swift @@ -113,43 +113,61 @@ private extension UpdatesViewController { func update(_ installedApp: InstalledApp) { - let toastView = RSTToastView(text: "Updating...", detailText: nil) - toastView.tintColor = .altPurple - toastView.activityIndicatorView.startAnimating() - toastView.show(in: self.navigationController?.view ?? self.view) - - let progress = AppManager.shared.install(installedApp.app, presentingViewController: self) { (result) in - do - { - let app = try result.get() - try app.managedObjectContext?.save() + func updateApp() + { + let toastView = RSTToastView(text: "Updating...", detailText: nil) + toastView.tintColor = .altPurple + toastView.activityIndicatorView.startAnimating() + toastView.show(in: self.navigationController?.view ?? self.view) + + let progress = AppManager.shared.install(installedApp.app, presentingViewController: self) { (result) in + do + { + let app = try result.get() + try app.managedObjectContext?.save() + + DispatchQueue.main.async { + let installedApp = DatabaseManager.shared.persistentContainer.viewContext.object(with: installedApp.objectID) as! InstalledApp + + let toastView = RSTToastView(text: "Updated \(installedApp.app.name) to version \(installedApp.version)!", detailText: nil) + toastView.tintColor = .altPurple + toastView.show(in: self.navigationController?.view ?? self.view, duration: 2) + + self.update() + } + } + catch + { + DispatchQueue.main.async { + let toastView = RSTToastView(text: "Failed to update \(installedApp.app.name)", detailText: error.localizedDescription) + toastView.tintColor = .altPurple + toastView.show(in: self.navigationController?.view ?? self.view, duration: 2) + } + } DispatchQueue.main.async { - let installedApp = DatabaseManager.shared.persistentContainer.viewContext.object(with: installedApp.objectID) as! InstalledApp - - let toastView = RSTToastView(text: "Updated \(installedApp.app.name) to version \(installedApp.version)!", detailText: nil) - toastView.tintColor = .altPurple - toastView.show(in: self.navigationController?.view ?? self.view, duration: 2) - - self.update() - } - } - catch - { - DispatchQueue.main.async { - let toastView = RSTToastView(text: "Failed to update \(installedApp.app.name)", detailText: error.localizedDescription) - toastView.tintColor = .altPurple - toastView.show(in: self.navigationController?.view ?? self.view, duration: 2) + self.progressView.observedProgress = nil + self.progressView.progress = 0.0 } } - DispatchQueue.main.async { - self.progressView.observedProgress = nil - self.progressView.progress = 0.0 - } + self.progressView.observedProgress = progress } - self.progressView.observedProgress = progress + if installedApp.app.identifier == App.altstoreAppID + { + let alertController = UIAlertController(title: NSLocalizedString("Update AltStore?", comment: ""), message: NSLocalizedString("AltStore will quit upon completion.", comment: ""), preferredStyle: .alert) + alertController.addAction(UIAlertAction(title: NSLocalizedString("Update and Quit", comment: ""), style: .default, handler: { (action) in + updateApp() + })) + alertController.addAction(.cancel) + + self.present(alertController, animated: true, completion: nil) + } + else + { + updateApp() + } } @objc func didFetchApps(_ notification: Notification)