mirror of
https://github.com/SideStore/SideStore.git
synced 2026-02-09 06:43:25 +01:00
Migrates database + cached apps from app sandbox to app group
This commit is contained in:
@@ -41,6 +41,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
|
||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool
|
||||
{
|
||||
// Register default settings before doing anything else.
|
||||
UserDefaults.registerDefaults()
|
||||
|
||||
DatabaseManager.shared.start { (error) in
|
||||
if let error = error
|
||||
{
|
||||
@@ -60,8 +63,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
|
||||
SecureValueTransformer.register()
|
||||
|
||||
UserDefaults.standard.registerDefaults()
|
||||
|
||||
if UserDefaults.standard.firstLaunch == nil
|
||||
{
|
||||
Keychain.shared.reset()
|
||||
|
||||
@@ -12,7 +12,15 @@ import Roxas
|
||||
|
||||
public extension UserDefaults
|
||||
{
|
||||
static let shared: UserDefaults = {
|
||||
guard let appGroup = Bundle.main.appGroups.first else { return .standard }
|
||||
|
||||
let sharedUserDefaults = UserDefaults(suiteName: appGroup)!
|
||||
return sharedUserDefaults
|
||||
}()
|
||||
|
||||
@NSManaged var firstLaunch: Date?
|
||||
@NSManaged var requiresAppGroupMigration: Bool
|
||||
|
||||
@NSManaged var preferredServerID: String?
|
||||
|
||||
@@ -42,16 +50,20 @@ public extension UserDefaults
|
||||
}
|
||||
@NSManaged @objc(activeAppsLimit) private var _activeAppsLimit: NSNumber?
|
||||
|
||||
func registerDefaults()
|
||||
class func registerDefaults()
|
||||
{
|
||||
let ios13_5 = OperatingSystemVersion(majorVersion: 13, minorVersion: 5, patchVersion: 0)
|
||||
let isLegacyDeactivationSupported = !ProcessInfo.processInfo.isOperatingSystemAtLeast(ios13_5)
|
||||
let activeAppLimitIncludesExtensions = !ProcessInfo.processInfo.isOperatingSystemAtLeast(ios13_5)
|
||||
|
||||
self.register(defaults: [
|
||||
let defaults = [
|
||||
#keyPath(UserDefaults.isBackgroundRefreshEnabled): true,
|
||||
#keyPath(UserDefaults.isLegacyDeactivationSupported): isLegacyDeactivationSupported,
|
||||
#keyPath(UserDefaults.activeAppLimitIncludesExtensions): activeAppLimitIncludesExtensions
|
||||
])
|
||||
#keyPath(UserDefaults.activeAppLimitIncludesExtensions): activeAppLimitIncludesExtensions,
|
||||
#keyPath(UserDefaults.requiresAppGroupMigration): true
|
||||
]
|
||||
|
||||
UserDefaults.standard.register(defaults: defaults)
|
||||
UserDefaults.shared.register(defaults: defaults)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import CoreData
|
||||
import AltSign
|
||||
import Roxas
|
||||
|
||||
private class PersistentContainer: RSTPersistentContainer
|
||||
fileprivate class PersistentContainer: RSTPersistentContainer
|
||||
{
|
||||
override class func defaultDirectoryURL() -> URL
|
||||
{
|
||||
@@ -22,6 +22,11 @@ private class PersistentContainer: RSTPersistentContainer
|
||||
|
||||
return databaseDirectoryURL
|
||||
}
|
||||
|
||||
class func legacyDirectoryURL() -> URL
|
||||
{
|
||||
return super.defaultDirectoryURL()
|
||||
}
|
||||
}
|
||||
|
||||
public class DatabaseManager
|
||||
@@ -35,6 +40,9 @@ public class DatabaseManager
|
||||
private var startCompletionHandlers = [(Error?) -> Void]()
|
||||
private let dispatchQueue = DispatchQueue(label: "io.altstore.DatabaseManager")
|
||||
|
||||
private let coordinator = NSFileCoordinator()
|
||||
private let coordinatorQueue = OperationQueue()
|
||||
|
||||
private init()
|
||||
{
|
||||
self.persistentContainer = PersistentContainer(name: "AltStore", bundle: Bundle(for: DatabaseManager.self))
|
||||
@@ -65,6 +73,11 @@ public extension DatabaseManager
|
||||
|
||||
guard !self.isStarted else { return finish(nil) }
|
||||
|
||||
self.migrateDatabaseToAppGroupIfNeeded { (result) in
|
||||
switch result
|
||||
{
|
||||
case .failure(let error): finish(error)
|
||||
case .success:
|
||||
self.persistentContainer.loadPersistentStores { (description, error) in
|
||||
guard error == nil else { return finish(error!) }
|
||||
|
||||
@@ -78,6 +91,8 @@ public extension DatabaseManager
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func signOut(completionHandler: @escaping (Error?) -> Void)
|
||||
{
|
||||
@@ -290,4 +305,75 @@ private extension DatabaseManager
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func migrateDatabaseToAppGroupIfNeeded(completion: @escaping (Result<Void, Error>) -> Void)
|
||||
{
|
||||
guard UserDefaults.shared.requiresAppGroupMigration else { return completion(.success(())) }
|
||||
|
||||
func finish(_ result: Result<Void, Error>)
|
||||
{
|
||||
switch result
|
||||
{
|
||||
case .failure(let error): completion(.failure(error))
|
||||
case .success:
|
||||
UserDefaults.shared.requiresAppGroupMigration = false
|
||||
completion(.success(()))
|
||||
}
|
||||
}
|
||||
|
||||
let previousDatabaseURL = PersistentContainer.legacyDirectoryURL().appendingPathComponent("AltStore.sqlite")
|
||||
let databaseURL = PersistentContainer.defaultDirectoryURL().appendingPathComponent("AltStore.sqlite")
|
||||
|
||||
let previousAppsDirectoryURL = InstalledApp.legacyAppsDirectoryURL
|
||||
let appsDirectoryURL = InstalledApp.appsDirectoryURL
|
||||
|
||||
let databaseIntent = NSFileAccessIntent.writingIntent(with: databaseURL, options: [.forReplacing])
|
||||
let appsIntent = NSFileAccessIntent.writingIntent(with: appsDirectoryURL, options: [.forReplacing])
|
||||
|
||||
self.coordinator.coordinate(with: [databaseIntent, appsIntent], queue: self.coordinatorQueue) { (error) in
|
||||
do
|
||||
{
|
||||
if let error = error
|
||||
{
|
||||
throw error
|
||||
}
|
||||
|
||||
let description = NSPersistentStoreDescription(url: previousDatabaseURL)
|
||||
|
||||
// Disable WAL to remove extra files automatically during migration.
|
||||
description.setOption(["journal_mode": "DELETE"] as NSDictionary, forKey: NSSQLitePragmasOption)
|
||||
|
||||
let persistentStoreCoordinator = NSPersistentStoreCoordinator(managedObjectModel: self.persistentContainer.managedObjectModel)
|
||||
|
||||
// Migrate database
|
||||
if FileManager.default.fileExists(atPath: previousDatabaseURL.path)
|
||||
{
|
||||
if FileManager.default.fileExists(atPath: databaseURL.path, isDirectory: nil)
|
||||
{
|
||||
try FileManager.default.removeItem(at: databaseURL)
|
||||
}
|
||||
|
||||
let previousDatabase = try persistentStoreCoordinator.addPersistentStore(ofType: description.type, configurationName: description.configuration, at: description.url, options: description.options)
|
||||
|
||||
// Pass nil options to prevent later error due to self.persistentContainer using WAL.
|
||||
try persistentStoreCoordinator.migratePersistentStore(previousDatabase, to: databaseURL, options: nil, withType: NSSQLiteStoreType)
|
||||
|
||||
try FileManager.default.removeItem(at: previousDatabaseURL)
|
||||
}
|
||||
|
||||
// Migrate apps
|
||||
if FileManager.default.fileExists(atPath: previousAppsDirectoryURL.path, isDirectory: nil)
|
||||
{
|
||||
_ = try FileManager.default.replaceItemAt(appsDirectoryURL, withItemAt: previousAppsDirectoryURL)
|
||||
}
|
||||
|
||||
finish(.success(()))
|
||||
}
|
||||
catch
|
||||
{
|
||||
print("Failed to migrate database to app group:", error)
|
||||
finish(.failure(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,6 +228,12 @@ public extension InstalledApp
|
||||
return appsDirectoryURL
|
||||
}
|
||||
|
||||
class var legacyAppsDirectoryURL: URL {
|
||||
let baseDirectory = FileManager.default.applicationSupportDirectory
|
||||
let appsDirectoryURL = baseDirectory.appendingPathComponent("Apps")
|
||||
return appsDirectoryURL
|
||||
}
|
||||
|
||||
class func fileURL(for app: AppProtocol) -> URL
|
||||
{
|
||||
let appURL = self.directoryURL(for: app).appendingPathComponent("App.app")
|
||||
|
||||
Reference in New Issue
Block a user