// // PersistenceService.swift // Persistence // // Created by Javier Cicchelli on 10/04/2023. // Copyright © 2023 Röck+Cöde. All rights reserved. // import CoreData public struct PersistenceService { // MARK: Properties public static let shared = PersistenceService() public static let inMemory = PersistenceService(inMemory: true) public let container: NSPersistentContainer // MARK: Initialisers init(inMemory: Bool = false) { guard let modelURL = Bundle.module.url(forResource: .Model.name, withExtension: .Model.extension), let managedObjectModel = NSManagedObjectModel(contentsOf: modelURL) else { fatalError("Could not load the model from the library.") } container = NSPersistentContainer( name: .Model.name, managedObjectModel: managedObjectModel ) setContainer(inMemory) } // MARK: Functions /// Create a private queue context. /// - Returns: A concurrent `NSManagedObjectContext` context instance ready to use. public func makeTaskContext() -> NSManagedObjectContext { let taskContext = container.newBackgroundContext() taskContext.automaticallyMergesChangesFromParent = true taskContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy return taskContext } /// Create a child context of the view context. /// - Returns: A generated child `NSManagedObjectContext` context instance ready to use. public func makeChildContext() -> NSManagedObjectContext { let context = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType) context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy context.parent = container.viewContext context.automaticallyMergesChangesFromParent = true return context } /// Save a given context, /// - Parameter context: A `NSManagedObjectContext` context instance to save. public func save(context: NSManagedObjectContext) { guard context.hasChanges else { return } do { try context.save() } catch { let nserror = error as NSError fatalError("Unresolved error \(nserror), \(nserror.userInfo)") } } /// Save a given child context as well as its respective parent context. /// - Parameter context: A child `NSManagedObjectContext` context instance to save. public func save(childContext context: NSManagedObjectContext) { guard context.hasChanges else { return } do { try context.save() guard let parent = context.parent, parent == container.viewContext else { return } try parent.performAndWait { try parent.save() } } catch { let nserror = error as NSError fatalError("Unresolved error \(nserror), \(nserror.userInfo)") } } } // MARK: - Helpers private extension PersistenceService { func setContainer(_ inMemory: Bool) { container.persistentStoreDescriptions = [ NSPersistentStoreDescription(url: inMemory ? URL(fileURLWithPath: "/dev/null") : NSPersistentContainer.defaultDirectoryURL().appending(path: "\(String.Model.name).sqlite") ) ] container.loadPersistentStores { _, error in if let error = error as NSError? { fatalError("Unresolved error \(error), \(error.userInfo)") } } container.viewContext.automaticallyMergesChangesFromParent = true } } // MARK: - String+Constants private extension String { enum Model { static let name = "Model" static let `extension` = "momd" } }