Hello
I have this Core Data stack and I have an observer to observe NSPersistentStoreRemoteChange, I would like to filter changes that the user has made to call the mergePersistentHistoryChanges() just when needed, I think it has to be done in the fetchPersistentHistoryTransactionsAndChanges() function but I don't know how to do it. Can you help me. Thank You
Here is my Core Data Stack:
class PersistenceController {
static let shared = PersistenceController()
private var notificationToken: NSObjectProtocol?
init() {
notificationToken = NotificationCenter.default.addObserver(forName: .NSPersistentStoreRemoteChange, object: nil, queue: nil) { note in
Task {
await self.fetchPersistentHistory()
}
}
}
deinit {
if let observer = notificationToken {
NotificationCenter.default.removeObserver(observer)
}
}
private var lastToken: NSPersistentHistoryToken?
/// A persistent container to set up the Core Data stack.
lazy var container: NSPersistentCloudKitContainer = {
let fileContainer = URL.storeURL(for: "group name", databaseName: "CoreData")
let container = NSPersistentCloudKitContainer(name: "CoreData")
let defaultDirectoryURL = NSPersistentContainer.defaultDirectoryURL()
let localStoreURL = defaultDirectoryURL.appendingPathComponent("Local.sqlite")
let localStoreDescription = NSPersistentStoreDescription(url: localStoreURL)
localStoreDescription.configuration = "Local"
// Create a store description for a CloudKit-backed local store
let cloudStoreDescription = NSPersistentStoreDescription(url: fileContainer)
cloudStoreDescription.configuration = "Cloud"
// Set the container options on the cloud store
cloudStoreDescription.cloudKitContainerOptions = NSPersistentCloudKitContainerOptions(containerIdentifier: "containerIdentifier")
cloudStoreDescription.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey)
cloudStoreDescription.setOption(true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey)
container.persistentStoreDescriptions = [cloudStoreDescription, localStoreDescription]
container.loadPersistentStores { storeDescription, error in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
}
// This sample refreshes UI by consuming store changes via persistent history tracking.
/// - Tag: viewContextMergeParentChanges
container.viewContext.automaticallyMergesChangesFromParent = false
container.viewContext.name = "viewContext"
container.viewContext.transactionAuthor = "User"
/// - Tag: viewContextMergePolicy
container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
container.viewContext.undoManager = nil
container.viewContext.shouldDeleteInaccessibleFaults = true
return container
}()
private func newTaskContext() -> NSManagedObjectContext {
// Create a private queue context.
/// - Tag: newBackgroundContext
let taskContext = container.newBackgroundContext()
taskContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
// Set unused undoManager to nil for macOS (it is nil by default on iOS)
// to reduce resource requirements.
taskContext.undoManager = nil
return taskContext
}
func save() {
if self.container.viewContext.hasChanges {
do {
try self.container.viewContext.save()
} catch {
print(Errors.errorSaving)
}
}
}
func fetchPersistentHistory() async {
do {
try await fetchPersistentHistoryTransactionsAndChanges()
} catch {
print(Errors.fetchPersistentHistory)
}
}
private func fetchPersistentHistoryTransactionsAndChanges() async throws {
let taskContext = newTaskContext()
taskContext.name = "persistentHistoryContext"
try await taskContext.perform {
let changeRequest = NSPersistentHistoryChangeRequest.fetchHistory(after: self.lastToken)
let historyResult = try taskContext.execute(changeRequest) as? NSPersistentHistoryResult
if let history = historyResult?.result as? [NSPersistentHistoryTransaction],
!history.isEmpty {
self.mergePersistentHistoryChanges(from: history)
return
}
throw Errors.fetchPersistentHistoryTransactionsAndChanges
}
}
private func mergePersistentHistoryChanges(from history: [NSPersistentHistoryTransaction]) {
let viewContext = container.viewContext
viewContext.perform {
for transaction in history {
print("Merged by func mergePersistentHistoryChanges ")
viewContext.mergeChanges(fromContextDidSave: transaction.objectIDNotification())
self.lastToken = transaction.token
}
}
}
}