Update UI in responds to remote changes of saved hashtags/instances

This commit is contained in:
Shadowfacts 2022-12-19 13:56:46 -05:00
parent d13b517128
commit 32be76ebee
1 changed files with 49 additions and 0 deletions

View File

@ -39,6 +39,9 @@ class MastodonCachePersistentStore: NSPersistentCloudKitContainer {
return context return context
}() }()
private var remoteChangeHandlerQueue = DispatchQueue(label: "PersistentStore remote changes")
private var lastRemoteChangeToken: NSPersistentHistoryToken?
// TODO: consider sending managed objects through this to avoid re-fetching things unnecessarily // TODO: consider sending managed objects through this to avoid re-fetching things unnecessarily
// would need to audit existing uses to make sure everything happens on the main thread // would need to audit existing uses to make sure everything happens on the main thread
// and when updating things on the background context would need to switch to main, refetch, and then publish // and when updating things on the background context would need to switch to main, refetch, and then publish
@ -63,6 +66,8 @@ class MastodonCachePersistentStore: NSPersistentCloudKitContainer {
localStoreLocation.appendPathComponent("\(accountInfo!.persistenceKey)_cache.sqlite", isDirectory: false) localStoreLocation.appendPathComponent("\(accountInfo!.persistenceKey)_cache.sqlite", isDirectory: false)
let localStoreDescription = NSPersistentStoreDescription(url: localStoreLocation) let localStoreDescription = NSPersistentStoreDescription(url: localStoreLocation)
localStoreDescription.configuration = "Local" localStoreDescription.configuration = "Local"
localStoreDescription.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey)
localStoreDescription.setOption(true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey)
var cloudStoreLocation = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first! var cloudStoreLocation = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first!
cloudStoreLocation.appendPathComponent("cloud.sqlite", isDirectory: false) cloudStoreLocation.appendPathComponent("cloud.sqlite", isDirectory: false)
@ -71,6 +76,8 @@ class MastodonCachePersistentStore: NSPersistentCloudKitContainer {
let options = NSPersistentCloudKitContainerOptions(containerIdentifier: "iCloud.space.vaccor.Tusker") let options = NSPersistentCloudKitContainerOptions(containerIdentifier: "iCloud.space.vaccor.Tusker")
options.databaseScope = .private options.databaseScope = .private
cloudStoreDescription.cloudKitContainerOptions = options cloudStoreDescription.cloudKitContainerOptions = options
cloudStoreDescription.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey)
cloudStoreDescription.setOption(true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey)
persistentStoreDescriptions = [ persistentStoreDescriptions = [
cloudStoreDescription, cloudStoreDescription,
@ -167,6 +174,7 @@ class MastodonCachePersistentStore: NSPersistentCloudKitContainer {
viewContext.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump viewContext.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump
NotificationCenter.default.addObserver(self, selector: #selector(managedObjectsDidChange), name: .NSManagedObjectContextObjectsDidChange, object: viewContext) NotificationCenter.default.addObserver(self, selector: #selector(managedObjectsDidChange), name: .NSManagedObjectContextObjectsDidChange, object: viewContext)
NotificationCenter.default.addObserver(self, selector: #selector(remoteChanges), name: .NSPersistentStoreRemoteChange, object: persistentStoreCoordinator)
} }
func save(context: NSManagedObjectContext) { func save(context: NSManagedObjectContext) {
@ -475,4 +483,45 @@ class MastodonCachePersistentStore: NSPersistentCloudKitContainer {
return changes return changes
} }
// the remote change notifications only handle deletes, inserts get handled by the regular managed object did change notifications
@objc private func remoteChanges(_ notification: Foundation.Notification) {
guard let token = notification.userInfo?[NSPersistentHistoryTokenKey] as? NSPersistentHistoryToken else {
return
}
remoteChangeHandlerQueue.async {
defer {
self.lastRemoteChangeToken = token
}
let req = NSPersistentHistoryChangeRequest.fetchHistory(after: self.lastRemoteChangeToken)
self.backgroundContext.performAndWait {
if let result = try? self.backgroundContext.execute(req) as? NSPersistentHistoryResult,
let transactions = result.result as? [NSPersistentHistoryTransaction] {
var changes: (hashtags: Bool, instances: Bool) = (false, false)
outer: for transaction in transactions {
for change in transaction.changes ?? [] {
if change.changedObjectID.entity.name == "SavedHashtag" {
changes.hashtags = true
} else if change.changedObjectID.entity.name == "SavedInstance" {
changes.instances = true
}
if changes.hashtags && changes.instances {
break outer
}
}
}
if changes.hashtags {
DispatchQueue.main.async {
NotificationCenter.default.post(name: .savedHashtagsChanged, object: nil)
}
}
if changes.instances {
DispatchQueue.main.async {
NotificationCenter.default.post(name: .savedInstancesChanged, object: nil)
}
}
}
}
}
}
} }