diff --git a/MastodonIntent/Handler/SendPostIntentHandler.swift b/MastodonIntent/Handler/SendPostIntentHandler.swift index b457b1c91..af56ad523 100644 --- a/MastodonIntent/Handler/SendPostIntentHandler.swift +++ b/MastodonIntent/Handler/SendPostIntentHandler.swift @@ -17,7 +17,7 @@ final class SendPostIntentHandler: NSObject { var disposeBag = Set() - let coreDataStack = CoreDataStack() + let coreDataStack = CoreDataStack(isInMemory: true) lazy var managedObjectContext = coreDataStack.persistentContainer.viewContext lazy var api: APIService = { let backgroundManagedObjectContext = coreDataStack.newTaskContext() diff --git a/MastodonSDK/Sources/CoreDataStack/CoreData.xcdatamodeld/CoreData 8.xcdatamodel/contents b/MastodonSDK/Sources/CoreDataStack/CoreData.xcdatamodeld/CoreData 8.xcdatamodel/contents index fe1fcd98a..b875fe28d 100644 --- a/MastodonSDK/Sources/CoreDataStack/CoreData.xcdatamodeld/CoreData 8.xcdatamodel/contents +++ b/MastodonSDK/Sources/CoreDataStack/CoreData.xcdatamodeld/CoreData 8.xcdatamodel/contents @@ -65,7 +65,7 @@ - + diff --git a/MastodonSDK/Sources/CoreDataStack/CoreDataStack.swift b/MastodonSDK/Sources/CoreDataStack/CoreDataStack.swift index 48e69e375..f7d8f1de7 100644 --- a/MastodonSDK/Sources/CoreDataStack/CoreDataStack.swift +++ b/MastodonSDK/Sources/CoreDataStack/CoreDataStack.swift @@ -22,10 +22,14 @@ public final class CoreDataStack { self.storeDescriptions = storeDescriptions } - public convenience init(databaseName: String = "shared") { + public convenience init(databaseName: String = "shared", isInMemory: Bool) { let storeURL = URL.storeURL(for: AppName.groupID, databaseName: databaseName) - let storeDescription = NSPersistentStoreDescription(url: storeURL) - storeDescription.url = URL(fileURLWithPath: "/dev/null") /// in-memory store with all features in favor of NSInMemoryStoreType + let storeDescription: NSPersistentStoreDescription + if isInMemory { + storeDescription = NSPersistentStoreDescription(url: URL(string: "file:///dev/null")!) /// in-memory store with all features in favor of NSInMemoryStoreType + } else { + storeDescription = NSPersistentStoreDescription(url: storeURL) + } self.init(persistentStoreDescriptions: [storeDescription]) } @@ -123,16 +127,18 @@ extension CoreDataStack { } } -extension CoreDataStack { - - public func rebuild() { +public extension CoreDataStack { + func tearDown() { let oldStoreURL = persistentContainer.persistentStoreCoordinator.url(for: persistentContainer.persistentStoreCoordinator.persistentStores.first!) try! persistentContainer.persistentStoreCoordinator.destroyPersistentStore(at: oldStoreURL, ofType: NSSQLiteStoreType, options: nil) + } + + func rebuild() { + tearDown() CoreDataStack.load(persistentContainer: persistentContainer) { [weak self] in guard let self = self else { return } self.didFinishLoad.value = true } } - } diff --git a/MastodonSDK/Sources/MastodonCore/AppContext.swift b/MastodonSDK/Sources/MastodonCore/AppContext.swift index d8aa06fae..02966e2f8 100644 --- a/MastodonSDK/Sources/MastodonCore/AppContext.swift +++ b/MastodonSDK/Sources/MastodonCore/AppContext.swift @@ -47,8 +47,22 @@ public class AppContext: ObservableObject { .eraseToAnyPublisher() public init() { - let _coreDataStack = CoreDataStack() + + let authProvider = AuthenticationServiceProvider.shared + let _coreDataStack: CoreDataStack + if authProvider.authenticationMigrationRequired { + _coreDataStack = CoreDataStack(isInMemory: false) + authProvider.migrateLegacyAuthentications( + in: _coreDataStack.persistentContainer.viewContext + ) + } else { + _coreDataStack = CoreDataStack(isInMemory: true) + } + let _managedObjectContext = _coreDataStack.persistentContainer.viewContext + _coreDataStack.persistentContainer.persistentStoreDescriptions.forEach { + $0.url = URL(fileURLWithPath: "/dev/null") + } let _backgroundManagedObjectContext = _coreDataStack.persistentContainer.newBackgroundContext() coreDataStack = _coreDataStack managedObjectContext = _managedObjectContext diff --git a/MastodonSDK/Sources/MastodonCore/AuthenticationServiceProvider.swift b/MastodonSDK/Sources/MastodonCore/AuthenticationServiceProvider.swift index 5a9c77228..f3cd504a0 100644 --- a/MastodonSDK/Sources/MastodonCore/AuthenticationServiceProvider.swift +++ b/MastodonSDK/Sources/MastodonCore/AuthenticationServiceProvider.swift @@ -6,10 +6,14 @@ import CoreDataStack import MastodonSDK import KeychainAccess import MastodonCommon +import os.log public class AuthenticationServiceProvider: ObservableObject { + private let logger = Logger(subsystem: "AuthenticationServiceProvider", category: "Authentication") + public static let shared = AuthenticationServiceProvider() private static let keychain = Keychain(service: "org.joinmastodon.app.authentications", accessGroup: AppName.groupID) + private let userDefaults: UserDefaults = .shared private init() {} @@ -63,6 +67,41 @@ public extension AuthenticationServiceProvider { return try? JSONDecoder().decode(MastodonAuthentication.self, from: data) } } + + func migrateLegacyAuthentications(in context: NSManagedObjectContext) { + do { + let legacyAuthentications = try context.fetch(MastodonAuthenticationLegacy.sortedFetchRequest) + let migratedAuthentications = legacyAuthentications.compactMap { auth -> MastodonAuthentication? in + return MastodonAuthentication( + identifier: auth.identifier, + domain: auth.domain, + username: auth.username, + appAccessToken: auth.appAccessToken, + userAccessToken: auth.userAccessToken, + clientID: auth.clientID, + clientSecret: auth.clientSecret, + createdAt: auth.createdAt, + updatedAt: auth.updatedAt, + activedAt: auth.activedAt, + userID: auth.userID + ) + } + + if migratedAuthentications.count != legacyAuthentications.count { + logger.log(level: .default, "Not all account authentications could be migrated.") + } + + self.authentications = migratedAuthentications + userDefaults.didMigrateAuthentications = true + } catch { + userDefaults.didMigrateAuthentications = false + logger.log(level: .error, "Could not migrate legacy authentications") + } + } + + var authenticationMigrationRequired: Bool { + userDefaults.didMigrateAuthentications == false + } } // MARK: - Private diff --git a/MastodonSDK/Sources/MastodonCore/UserDefaults+Authentication.swift b/MastodonSDK/Sources/MastodonCore/UserDefaults+Authentication.swift new file mode 100644 index 000000000..d5262027c --- /dev/null +++ b/MastodonSDK/Sources/MastodonCore/UserDefaults+Authentication.swift @@ -0,0 +1,20 @@ +// Copyright © 2023 Mastodon gGmbH. All rights reserved. + +import Foundation + +public extension UserDefaults { + + enum Keys { + static let didMigrateAuthenticationsKey = "didMigrateAuthentications" + } + + @objc dynamic var didMigrateAuthentications: Bool { + get { + return bool(forKey: Keys.didMigrateAuthenticationsKey) + } + + set { + set(newValue, forKey: Keys.didMigrateAuthenticationsKey) + } + } +}