Prioritizes app refresh order

Tries to refresh apps that are about to expire first, and then always refreshes AltStore itself last, since refreshing AltStore means that the app will quit.
This commit is contained in:
Riley Testut
2019-06-21 11:20:03 -07:00
parent c096fd02b4
commit 39c84e623a
21 changed files with 1016 additions and 621 deletions

View File

@@ -63,7 +63,7 @@ extension AppDelegate
{
private func prepareForBackgroundFetch()
{
// Fetch every 6 hours.
// "Fetch" every hour, but then refresh only those that need to be refreshed (so we don't drain the battery).
UIApplication.shared.setMinimumBackgroundFetchInterval(60 * 60)
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { (success, error) in
@@ -72,49 +72,100 @@ extension AppDelegate
func application(_ application: UIApplication, performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void)
{
let installedApps = InstalledApp.fetchAppsForBackgroundRefresh(in: DatabaseManager.shared.viewContext)
guard !installedApps.isEmpty else { return completionHandler(.noData) }
print("Apps to refresh:", installedApps.map { $0.app.identifier })
ServerManager.shared.startDiscovering()
let content = UNMutableNotificationContent()
content.title = NSLocalizedString("Refreshing apps...", comment: "")
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 0.01, repeats: false)
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request) { (error) in
if let error = error {
print(error)
}
}
// Wait a few seconds so we have a chance to discover nearby AltServers.
DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) {
let installedApps = InstalledApp.all(in: DatabaseManager.shared.viewContext)
_ = AppManager.shared.refresh(installedApps, presentingViewController: nil) { (result) in
func finish(_ result: Result<[String: Result<InstalledApp, Error>], Error>)
{
ServerManager.shared.stopDiscovering()
let content = UNMutableNotificationContent()
var shouldPresentAlert = true
do
{
let results = try result.get()
shouldPresentAlert = !results.isEmpty
for (_, result) in results
{
guard case let .failure(error) = result else { continue }
throw error
}
content.title = "Refreshed Apps!"
content.body = "Successfully refreshed all apps."
completionHandler(.newData)
content.title = NSLocalizedString("Refreshed all apps!", comment: "")
}
catch
{
print("Failed to refresh apps in background.", error)
content.title = "Failed to Refresh Apps"
content.title = NSLocalizedString("Failed to Refresh Apps", comment: "")
content.body = error.localizedDescription
completionHandler(.failed)
shouldPresentAlert = true
}
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 0.1, repeats: false)
let request = UNNotificationRequest(identifier: "RefreshedApps", content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request) { (error) in
if let error = error {
print(error)
if shouldPresentAlert
{
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 0.01, repeats: false)
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request) { (error) in
if let error = error {
print(error)
}
}
}
switch result
{
case .failure(ConnectionError.serverNotFound): completionHandler(.newData)
case .failure: completionHandler(.failed)
case .success: completionHandler(.newData)
}
}
let group = AppManager.shared.refresh(installedApps, presentingViewController: nil)
group.beginInstallationHandler = { (installedApp) in
guard installedApp.app.identifier == App.altstoreAppID else { return }
// We're starting to install AltStore, which means the app is about to quit.
// So, we say we were successful even though we technically don't know 100% yet.
// Also since AltServer has already received the app, it can finish installing even if we're no longer running in background.
if let error = group.error
{
finish(.failure(error))
}
else
{
var results = group.results
results[installedApp.app.identifier] = .success(installedApp)
finish(.success(results))
}
}
group.completionHandler = { (result) in
finish(result)
}
}
}