2019-05-09 12:45:30 -07:00
|
|
|
//
|
|
|
|
|
// AppDelegate.swift
|
|
|
|
|
// AltStore
|
|
|
|
|
//
|
|
|
|
|
// Created by Riley Testut on 5/9/19.
|
|
|
|
|
// Copyright © 2019 Riley Testut. All rights reserved.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
import UIKit
|
2019-06-04 18:29:50 -07:00
|
|
|
import UserNotifications
|
2019-07-01 17:13:55 -07:00
|
|
|
import AVFoundation
|
2020-09-08 12:29:44 -07:00
|
|
|
import Intents
|
2019-05-09 12:45:30 -07:00
|
|
|
|
2020-09-03 16:39:08 -07:00
|
|
|
import AltStoreCore
|
2019-05-30 17:10:50 -07:00
|
|
|
import AltSign
|
|
|
|
|
import Roxas
|
|
|
|
|
|
2019-09-19 14:43:26 -07:00
|
|
|
extension AppDelegate
|
|
|
|
|
{
|
2019-09-27 17:39:36 -07:00
|
|
|
static let openPatreonSettingsDeepLinkNotification = Notification.Name("com.rileytestut.AltStore.OpenPatreonSettingsDeepLinkNotification")
|
|
|
|
|
static let importAppDeepLinkNotification = Notification.Name("com.rileytestut.AltStore.ImportAppDeepLinkNotification")
|
2020-08-28 12:15:15 -07:00
|
|
|
static let addSourceDeepLinkNotification = Notification.Name("com.rileytestut.AltStore.AddSourceDeepLinkNotification")
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2020-05-15 15:11:17 -07:00
|
|
|
static let appBackupDidFinish = Notification.Name("com.rileytestut.AltStore.AppBackupDidFinish")
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2019-09-27 17:39:36 -07:00
|
|
|
static let importAppDeepLinkURLKey = "fileURL"
|
2020-05-15 15:11:17 -07:00
|
|
|
static let appBackupResultKey = "result"
|
2020-08-28 12:15:15 -07:00
|
|
|
static let addSourceDeepLinkURLKey = "sourceURL"
|
2019-09-19 14:43:26 -07:00
|
|
|
}
|
|
|
|
|
|
2019-05-09 12:45:30 -07:00
|
|
|
@UIApplicationMain
|
|
|
|
|
class AppDelegate: UIResponder, UIApplicationDelegate {
|
|
|
|
|
|
|
|
|
|
var window: UIWindow?
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2020-09-15 15:19:12 -07:00
|
|
|
@available(iOS 14, *)
|
2022-08-17 15:22:43 -05:00
|
|
|
private var intentHandler: IntentHandler {
|
|
|
|
|
get { _intentHandler as! IntentHandler }
|
|
|
|
|
set { _intentHandler = newValue }
|
|
|
|
|
}
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2020-09-15 15:19:12 -07:00
|
|
|
@available(iOS 14, *)
|
2022-08-17 15:22:43 -05:00
|
|
|
private var viewAppIntentHandler: ViewAppIntentHandler {
|
|
|
|
|
get { _viewAppIntentHandler as! ViewAppIntentHandler }
|
|
|
|
|
set { _viewAppIntentHandler = newValue }
|
|
|
|
|
}
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2022-08-17 15:22:43 -05:00
|
|
|
private lazy var _intentHandler: Any = {
|
|
|
|
|
guard #available(iOS 14, *) else { fatalError() }
|
|
|
|
|
return IntentHandler()
|
|
|
|
|
}()
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2022-08-17 15:22:43 -05:00
|
|
|
private lazy var _viewAppIntentHandler: Any = {
|
|
|
|
|
guard #available(iOS 14, *) else { fatalError() }
|
|
|
|
|
return ViewAppIntentHandler()
|
|
|
|
|
}()
|
2019-05-09 12:45:30 -07:00
|
|
|
|
2019-05-20 21:24:53 +02:00
|
|
|
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool
|
|
|
|
|
{
|
2020-09-16 12:09:12 -07:00
|
|
|
// Register default settings before doing anything else.
|
|
|
|
|
UserDefaults.registerDefaults()
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2020-07-13 17:59:52 -07:00
|
|
|
DatabaseManager.shared.start { (error) in
|
|
|
|
|
if let error = error
|
|
|
|
|
{
|
|
|
|
|
print("Failed to start DatabaseManager. Error:", error as Any)
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
print("Started DatabaseManager.")
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2020-03-31 14:31:34 -07:00
|
|
|
AnalyticsManager.shared.start()
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2019-07-24 12:23:54 -07:00
|
|
|
self.setTintColor()
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2019-05-30 17:10:50 -07:00
|
|
|
ServerManager.shared.startDiscovering()
|
2022-08-19 01:14:16 -04:00
|
|
|
|
|
|
|
|
SecureValueTransformer.register()
|
|
|
|
|
|
2019-06-04 18:29:50 -07:00
|
|
|
if UserDefaults.standard.firstLaunch == nil
|
|
|
|
|
{
|
2019-06-06 14:46:23 -07:00
|
|
|
Keychain.shared.reset()
|
2019-06-04 18:29:50 -07:00
|
|
|
UserDefaults.standard.firstLaunch = Date()
|
|
|
|
|
}
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2019-09-03 23:47:47 -07:00
|
|
|
UserDefaults.standard.preferredServerID = Bundle.main.object(forInfoDictionaryKey: Bundle.Info.serverID) as? String
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2019-09-19 22:20:10 -07:00
|
|
|
#if DEBUG || BETA
|
|
|
|
|
UserDefaults.standard.isDebugModeEnabled = true
|
|
|
|
|
#endif
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2019-06-04 18:29:50 -07:00
|
|
|
self.prepareForBackgroundFetch()
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2019-05-09 12:45:30 -07:00
|
|
|
return true
|
|
|
|
|
}
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2019-05-30 17:10:50 -07:00
|
|
|
func applicationDidEnterBackground(_ application: UIApplication)
|
|
|
|
|
{
|
|
|
|
|
ServerManager.shared.stopDiscovering()
|
2019-05-09 12:45:30 -07:00
|
|
|
}
|
|
|
|
|
|
2019-05-30 17:10:50 -07:00
|
|
|
func applicationWillEnterForeground(_ application: UIApplication)
|
|
|
|
|
{
|
2019-06-04 18:29:50 -07:00
|
|
|
AppManager.shared.update()
|
2019-05-30 17:10:50 -07:00
|
|
|
ServerManager.shared.startDiscovering()
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2019-08-28 11:13:22 -07:00
|
|
|
PatreonAPI.shared.refreshPatreonAccount()
|
2019-05-09 12:45:30 -07:00
|
|
|
}
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2019-09-19 14:43:26 -07:00
|
|
|
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any]) -> Bool
|
|
|
|
|
{
|
|
|
|
|
return self.open(url)
|
|
|
|
|
}
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2020-09-08 12:29:44 -07:00
|
|
|
func application(_ application: UIApplication, handlerFor intent: INIntent) -> Any?
|
|
|
|
|
{
|
2020-09-15 15:19:12 -07:00
|
|
|
guard #available(iOS 14, *) else { return nil }
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2020-09-15 15:19:12 -07:00
|
|
|
switch intent
|
|
|
|
|
{
|
|
|
|
|
case is RefreshAllIntent: return self.intentHandler
|
|
|
|
|
case is ViewAppIntent: return self.viewAppIntentHandler
|
|
|
|
|
default: return nil
|
|
|
|
|
}
|
2020-09-08 12:29:44 -07:00
|
|
|
}
|
2019-06-04 18:29:50 -07:00
|
|
|
}
|
2019-05-09 12:45:30 -07:00
|
|
|
|
2020-07-13 17:59:52 -07:00
|
|
|
@available(iOS 13, *)
|
|
|
|
|
extension AppDelegate
|
|
|
|
|
{
|
|
|
|
|
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration
|
|
|
|
|
{
|
|
|
|
|
// Called when a new scene session is being created.
|
|
|
|
|
// Use this method to select a configuration to create the new scene with.
|
|
|
|
|
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
|
|
|
|
|
}
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2020-07-13 17:59:52 -07:00
|
|
|
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>)
|
|
|
|
|
{
|
|
|
|
|
// Called when the user discards a scene session.
|
|
|
|
|
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
|
|
|
|
|
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-24 12:23:54 -07:00
|
|
|
private extension AppDelegate
|
|
|
|
|
{
|
|
|
|
|
func setTintColor()
|
|
|
|
|
{
|
2019-09-19 11:29:10 -07:00
|
|
|
self.window?.tintColor = .altPrimary
|
2019-07-24 12:23:54 -07:00
|
|
|
}
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2019-09-19 14:43:26 -07:00
|
|
|
func open(_ url: URL) -> Bool
|
|
|
|
|
{
|
2019-09-27 17:39:36 -07:00
|
|
|
if url.isFileURL
|
|
|
|
|
{
|
|
|
|
|
guard url.pathExtension.lowercased() == "ipa" else { return false }
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2019-09-27 17:39:36 -07:00
|
|
|
DispatchQueue.main.async {
|
|
|
|
|
NotificationCenter.default.post(name: AppDelegate.importAppDeepLinkNotification, object: nil, userInfo: [AppDelegate.importAppDeepLinkURLKey: url])
|
|
|
|
|
}
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2019-09-27 17:39:36 -07:00
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false) else { return false }
|
2020-05-15 15:11:17 -07:00
|
|
|
guard let host = components.host?.lowercased() else { return false }
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2020-05-15 15:11:17 -07:00
|
|
|
switch host
|
|
|
|
|
{
|
|
|
|
|
case "patreon":
|
|
|
|
|
DispatchQueue.main.async {
|
|
|
|
|
NotificationCenter.default.post(name: AppDelegate.openPatreonSettingsDeepLinkNotification, object: nil)
|
|
|
|
|
}
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2020-05-15 15:11:17 -07:00
|
|
|
return true
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2020-05-15 15:11:17 -07:00
|
|
|
case "appbackupresponse":
|
|
|
|
|
let result: Result<Void, Error>
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2020-05-15 15:11:17 -07:00
|
|
|
switch url.path.lowercased()
|
|
|
|
|
{
|
|
|
|
|
case "/success": result = .success(())
|
|
|
|
|
case "/failure":
|
|
|
|
|
let queryItems = components.queryItems?.reduce(into: [String: String]()) { $0[$1.name] = $1.value } ?? [:]
|
|
|
|
|
guard
|
|
|
|
|
let errorDomain = queryItems["errorDomain"],
|
|
|
|
|
let errorCodeString = queryItems["errorCode"], let errorCode = Int(errorCodeString),
|
|
|
|
|
let errorDescription = queryItems["errorDescription"]
|
|
|
|
|
else { return false }
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2020-05-15 15:11:17 -07:00
|
|
|
let error = NSError(domain: errorDomain, code: errorCode, userInfo: [NSLocalizedDescriptionKey: errorDescription])
|
|
|
|
|
result = .failure(error)
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2020-05-15 15:11:17 -07:00
|
|
|
default: return false
|
|
|
|
|
}
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2020-05-15 15:11:17 -07:00
|
|
|
NotificationCenter.default.post(name: AppDelegate.appBackupDidFinish, object: nil, userInfo: [AppDelegate.appBackupResultKey: result])
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2020-05-15 15:11:17 -07:00
|
|
|
return true
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2020-05-17 23:44:36 -07:00
|
|
|
case "install":
|
|
|
|
|
let queryItems = components.queryItems?.reduce(into: [String: String]()) { $0[$1.name.lowercased()] = $1.value } ?? [:]
|
|
|
|
|
guard let downloadURLString = queryItems["url"], let downloadURL = URL(string: downloadURLString) else { return false }
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2020-05-17 23:44:36 -07:00
|
|
|
DispatchQueue.main.async {
|
|
|
|
|
NotificationCenter.default.post(name: AppDelegate.importAppDeepLinkNotification, object: nil, userInfo: [AppDelegate.importAppDeepLinkURLKey: downloadURL])
|
|
|
|
|
}
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2020-05-17 23:44:36 -07:00
|
|
|
return true
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2020-08-28 12:15:15 -07:00
|
|
|
case "source":
|
|
|
|
|
let queryItems = components.queryItems?.reduce(into: [String: String]()) { $0[$1.name.lowercased()] = $1.value } ?? [:]
|
|
|
|
|
guard let sourceURLString = queryItems["url"], let sourceURL = URL(string: sourceURLString) else { return false }
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2020-08-28 12:15:15 -07:00
|
|
|
DispatchQueue.main.async {
|
|
|
|
|
NotificationCenter.default.post(name: AppDelegate.addSourceDeepLinkNotification, object: nil, userInfo: [AppDelegate.addSourceDeepLinkURLKey: sourceURL])
|
|
|
|
|
}
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2020-08-28 12:15:15 -07:00
|
|
|
return true
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2020-05-15 15:11:17 -07:00
|
|
|
default: return false
|
2019-09-27 17:39:36 -07:00
|
|
|
}
|
2019-09-19 14:43:26 -07:00
|
|
|
}
|
|
|
|
|
}
|
2019-07-24 12:23:54 -07:00
|
|
|
}
|
|
|
|
|
|
2019-06-04 18:29:50 -07:00
|
|
|
extension AppDelegate
|
|
|
|
|
{
|
|
|
|
|
private func prepareForBackgroundFetch()
|
|
|
|
|
{
|
2019-06-21 11:20:03 -07:00
|
|
|
// "Fetch" every hour, but then refresh only those that need to be refreshed (so we don't drain the battery).
|
2019-07-24 13:16:53 -07:00
|
|
|
UIApplication.shared.setMinimumBackgroundFetchInterval(1 * 60 * 60)
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2019-06-04 18:29:50 -07:00
|
|
|
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { (success, error) in
|
|
|
|
|
}
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2019-07-01 17:14:51 -07:00
|
|
|
#if DEBUG
|
|
|
|
|
UIApplication.shared.registerForRemoteNotifications()
|
|
|
|
|
#endif
|
|
|
|
|
}
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2019-07-01 17:14:51 -07:00
|
|
|
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data)
|
|
|
|
|
{
|
|
|
|
|
let tokenParts = deviceToken.map { data -> String in
|
|
|
|
|
return String(format: "%02.2hhx", data)
|
|
|
|
|
}
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2019-07-01 17:14:51 -07:00
|
|
|
let token = tokenParts.joined()
|
|
|
|
|
print("Push Token:", token)
|
|
|
|
|
}
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2019-07-01 17:14:51 -07:00
|
|
|
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void)
|
|
|
|
|
{
|
|
|
|
|
self.application(application, performFetchWithCompletionHandler: completionHandler)
|
2019-05-09 12:45:30 -07:00
|
|
|
}
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2019-07-31 13:35:12 -07:00
|
|
|
func application(_ application: UIApplication, performFetchWithCompletionHandler backgroundFetchCompletionHandler: @escaping (UIBackgroundFetchResult) -> Void)
|
2019-06-04 18:29:50 -07:00
|
|
|
{
|
2020-09-08 12:29:44 -07:00
|
|
|
if UserDefaults.standard.isBackgroundRefreshEnabled && !UserDefaults.standard.presentedLaunchReminderNotification
|
2019-09-05 11:51:49 -07:00
|
|
|
{
|
2020-09-08 12:29:44 -07:00
|
|
|
let threeHours: TimeInterval = 3 * 60 * 60
|
|
|
|
|
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: threeHours, repeats: false)
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2020-09-08 12:29:44 -07:00
|
|
|
let content = UNMutableNotificationContent()
|
|
|
|
|
content.title = NSLocalizedString("App Refresh Tip", comment: "")
|
|
|
|
|
content.body = NSLocalizedString("The more you open AltStore, the more chances it's given to refresh apps in the background.", comment: "")
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2020-09-08 12:29:44 -07:00
|
|
|
let request = UNNotificationRequest(identifier: "background-refresh-reminder5", content: content, trigger: trigger)
|
|
|
|
|
UNUserNotificationCenter.current().add(request)
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2020-09-08 12:29:44 -07:00
|
|
|
UserDefaults.standard.presentedLaunchReminderNotification = true
|
2019-09-05 11:51:49 -07:00
|
|
|
}
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2019-06-21 11:33:12 -07:00
|
|
|
BackgroundTaskManager.shared.performExtendedBackgroundTask { (taskResult, taskCompletionHandler) in
|
2019-07-24 13:16:53 -07:00
|
|
|
if let error = taskResult.error
|
|
|
|
|
{
|
|
|
|
|
print("Error starting extended background task. Aborting.", error)
|
2019-07-31 13:35:12 -07:00
|
|
|
backgroundFetchCompletionHandler(.failed)
|
2020-09-08 12:29:44 -07:00
|
|
|
taskCompletionHandler()
|
2019-07-24 13:16:53 -07:00
|
|
|
return
|
|
|
|
|
}
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2019-07-31 13:35:12 -07:00
|
|
|
if !DatabaseManager.shared.isStarted
|
|
|
|
|
{
|
|
|
|
|
DatabaseManager.shared.start() { (error) in
|
2020-09-08 12:29:44 -07:00
|
|
|
if error != nil
|
2019-07-31 13:35:12 -07:00
|
|
|
{
|
|
|
|
|
backgroundFetchCompletionHandler(.failed)
|
2020-09-08 12:29:44 -07:00
|
|
|
taskCompletionHandler()
|
2019-07-31 13:35:12 -07:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2020-09-08 12:29:44 -07:00
|
|
|
self.performBackgroundFetch { (backgroundFetchResult) in
|
|
|
|
|
backgroundFetchCompletionHandler(backgroundFetchResult)
|
|
|
|
|
} refreshAppsCompletionHandler: { (refreshAppsResult) in
|
|
|
|
|
taskCompletionHandler()
|
|
|
|
|
}
|
2019-07-31 13:35:12 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2020-09-08 12:29:44 -07:00
|
|
|
self.performBackgroundFetch { (backgroundFetchResult) in
|
|
|
|
|
backgroundFetchCompletionHandler(backgroundFetchResult)
|
|
|
|
|
} refreshAppsCompletionHandler: { (refreshAppsResult) in
|
|
|
|
|
taskCompletionHandler()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2020-09-08 12:29:44 -07:00
|
|
|
func performBackgroundFetch(backgroundFetchCompletionHandler: @escaping (UIBackgroundFetchResult) -> Void,
|
|
|
|
|
refreshAppsCompletionHandler: @escaping (Result<[String: Result<InstalledApp, Error>], Error>) -> Void)
|
|
|
|
|
{
|
|
|
|
|
self.fetchSources { (result) in
|
|
|
|
|
switch result
|
|
|
|
|
{
|
|
|
|
|
case .failure: backgroundFetchCompletionHandler(.failed)
|
|
|
|
|
case .success: backgroundFetchCompletionHandler(.newData)
|
2019-07-31 13:35:12 -07:00
|
|
|
}
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2020-09-08 12:29:44 -07:00
|
|
|
if !UserDefaults.standard.isBackgroundRefreshEnabled
|
|
|
|
|
{
|
|
|
|
|
refreshAppsCompletionHandler(.success([:]))
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2020-09-08 12:29:44 -07:00
|
|
|
guard UserDefaults.standard.isBackgroundRefreshEnabled else { return }
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2020-09-08 12:29:44 -07:00
|
|
|
DatabaseManager.shared.persistentContainer.performBackgroundTask { (context) in
|
|
|
|
|
let installedApps = InstalledApp.fetchAppsForBackgroundRefresh(in: context)
|
|
|
|
|
AppManager.shared.backgroundRefresh(installedApps, completionHandler: refreshAppsCompletionHandler)
|
2019-07-31 13:35:12 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private extension AppDelegate
|
|
|
|
|
{
|
2020-09-08 12:29:44 -07:00
|
|
|
func fetchSources(completionHandler: @escaping (Result<Set<Source>, Error>) -> Void)
|
2019-07-31 13:35:12 -07:00
|
|
|
{
|
2020-03-24 13:27:44 -07:00
|
|
|
AppManager.shared.fetchSources() { (result) in
|
2019-09-05 11:51:49 -07:00
|
|
|
do
|
|
|
|
|
{
|
2020-09-08 12:29:44 -07:00
|
|
|
let (sources, context) = try result.get()
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2019-09-05 11:51:49 -07:00
|
|
|
let previousUpdatesFetchRequest = InstalledApp.updatesFetchRequest() as! NSFetchRequest<NSFetchRequestResult>
|
|
|
|
|
previousUpdatesFetchRequest.includesPendingChanges = false
|
|
|
|
|
previousUpdatesFetchRequest.resultType = .dictionaryResultType
|
|
|
|
|
previousUpdatesFetchRequest.propertiesToFetch = [#keyPath(InstalledApp.bundleIdentifier)]
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2019-09-06 14:41:30 -07:00
|
|
|
let previousNewsItemsFetchRequest = NewsItem.fetchRequest() as NSFetchRequest<NSFetchRequestResult>
|
|
|
|
|
previousNewsItemsFetchRequest.includesPendingChanges = false
|
|
|
|
|
previousNewsItemsFetchRequest.resultType = .dictionaryResultType
|
|
|
|
|
previousNewsItemsFetchRequest.propertiesToFetch = [#keyPath(NewsItem.identifier)]
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2019-09-05 11:51:49 -07:00
|
|
|
let previousUpdates = try context.fetch(previousUpdatesFetchRequest) as! [[String: String]]
|
2019-09-06 14:41:30 -07:00
|
|
|
let previousNewsItems = try context.fetch(previousNewsItemsFetchRequest) as! [[String: String]]
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2019-09-05 11:51:49 -07:00
|
|
|
try context.save()
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2019-09-05 11:51:49 -07:00
|
|
|
let updatesFetchRequest = InstalledApp.updatesFetchRequest()
|
2019-09-06 14:41:30 -07:00
|
|
|
let newsItemsFetchRequest = NewsItem.fetchRequest() as NSFetchRequest<NewsItem>
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2019-09-05 11:51:49 -07:00
|
|
|
let updates = try context.fetch(updatesFetchRequest)
|
2019-09-06 14:41:30 -07:00
|
|
|
let newsItems = try context.fetch(newsItemsFetchRequest)
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2019-09-05 11:51:49 -07:00
|
|
|
for update in updates
|
2019-07-31 13:35:12 -07:00
|
|
|
{
|
2019-09-05 11:51:49 -07:00
|
|
|
guard !previousUpdates.contains(where: { $0[#keyPath(InstalledApp.bundleIdentifier)] == update.bundleIdentifier }) else { continue }
|
|
|
|
|
guard let storeApp = update.storeApp else { continue }
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2019-09-05 11:51:49 -07:00
|
|
|
let content = UNMutableNotificationContent()
|
|
|
|
|
content.title = NSLocalizedString("New Update Available", comment: "")
|
|
|
|
|
content.body = String(format: NSLocalizedString("%@ %@ is now available for download.", comment: ""), update.name, storeApp.version)
|
2019-09-21 22:30:01 -07:00
|
|
|
content.sound = .default
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2019-09-05 11:51:49 -07:00
|
|
|
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: nil)
|
|
|
|
|
UNUserNotificationCenter.current().add(request)
|
|
|
|
|
}
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2019-09-06 14:41:30 -07:00
|
|
|
for newsItem in newsItems
|
|
|
|
|
{
|
|
|
|
|
guard !previousNewsItems.contains(where: { $0[#keyPath(NewsItem.identifier)] == newsItem.identifier }) else { continue }
|
|
|
|
|
guard !newsItem.isSilent else { continue }
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2019-09-06 14:41:30 -07:00
|
|
|
let content = UNMutableNotificationContent()
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2019-09-06 14:41:30 -07:00
|
|
|
if let app = newsItem.storeApp
|
|
|
|
|
{
|
|
|
|
|
content.title = String(format: NSLocalizedString("%@ News", comment: ""), app.name)
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
content.title = NSLocalizedString("AltStore News", comment: "")
|
|
|
|
|
}
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2019-09-06 14:41:30 -07:00
|
|
|
content.body = newsItem.title
|
2019-09-21 22:30:01 -07:00
|
|
|
content.sound = .default
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2019-09-06 14:41:30 -07:00
|
|
|
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: nil)
|
|
|
|
|
UNUserNotificationCenter.current().add(request)
|
|
|
|
|
}
|
2019-09-05 11:51:49 -07:00
|
|
|
|
|
|
|
|
DispatchQueue.main.async {
|
|
|
|
|
UIApplication.shared.applicationIconBadgeNumber = updates.count
|
2019-07-31 13:35:12 -07:00
|
|
|
}
|
2022-08-19 01:14:16 -04:00
|
|
|
|
2020-09-08 12:29:44 -07:00
|
|
|
completionHandler(.success(sources))
|
2019-07-31 13:35:12 -07:00
|
|
|
}
|
|
|
|
|
catch
|
|
|
|
|
{
|
2020-09-08 12:29:44 -07:00
|
|
|
print("Error fetching apps:", error)
|
|
|
|
|
completionHandler(.failure(error))
|
2019-07-24 13:16:53 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-05-09 12:45:30 -07:00
|
|
|
}
|