Impressia/CoreData/CoreDataHandler.swift

100 lines
4.4 KiB
Swift

//
// https://mczachurski.dev
// Copyright © 2023 Marcin Czachurski and the repository contributors.
// Licensed under the Apache License 2.0.
//
import CoreData
import OSLog
import EnvironmentKit
public class CoreDataHandler {
public static let shared = CoreDataHandler()
lazy var container: NSPersistentContainer = {
let container = NSPersistentContainer(name: AppConstants.coreDataPersistantContainerName)
let storeURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.dev.mczachurski.vernissage")!
.appendingPathComponent("Data.sqlite")
var defaultURL: URL?
if let storeDescription = container.persistentStoreDescriptions.first, let url = storeDescription.url {
defaultURL = FileManager.default.fileExists(atPath: url.path) ? url : nil
}
if defaultURL == nil {
container.persistentStoreDescriptions = [NSPersistentStoreDescription(url: storeURL)]
}
container.loadPersistentStores(completionHandler: { [unowned container] (_, error) in
if let error = error as NSError? {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate.
// You should not use this function in a shipping application, although it may be useful during development.
/*
Typical reasons for an error here include:
* The parent directory does not exist, cannot be created, or disallows writing.
* The persistent store is not accessible, due to permissions or data protection when the device is locked.
* The device is out of space.
* The store could not be migrated to the current model version.
Check the error message to determine what the actual problem was.
*/
fatalError("Unresolved error \(error), \(error.userInfo)")
}
// Migrate old store do current (shared between app and widget)
if let url = defaultURL, url.absoluteString != storeURL.absoluteString {
let coordinator = container.persistentStoreCoordinator
if let oldStore = coordinator.persistentStore(for: url) {
// Migration process.
do {
try coordinator.migratePersistentStore(oldStore, to: storeURL, options: nil, withType: NSSQLiteStoreType)
} catch {
Logger.main.error("\(error.localizedDescription)")
}
// Delete old store.
let fileCoordinator = NSFileCoordinator(filePresenter: nil)
fileCoordinator.coordinate(writingItemAt: url, options: .forDeleting, error: nil, byAccessor: { url in
do {
try FileManager.default.removeItem(at: url)
} catch {
Logger.main.error("\(error.localizedDescription)")
}
})
}
}
})
container.viewContext.automaticallyMergesChangesFromParent = true
return container
}()
public func newBackgroundContext() -> NSManagedObjectContext {
self.container.newBackgroundContext()
}
public func save(viewContext: NSManagedObjectContext? = nil) {
let context = viewContext ?? CoreDataHandler.shared.container.viewContext
if context.hasChanges {
context.performAndWait {
do {
try context.save()
} catch {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate.
// You should not use this function in a shipping application, although it may be useful during development.
#if DEBUG
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
#else
CoreDataError.shared.handle(error, message: "An error occurred while writing the data.")
#endif
}
}
}
}
}