ios - iOS - 通知今天扩展主应用程序中的核心数据更改
问题描述
我有一个在主机应用程序和今天的扩展程序之间共享的NSManagedObject
呼叫。Event
(在 Target Membership 中,主应用程序和小部件都被选中)。
主机应用程序和小部件具有相同的应用程序组标识符并且都共享Data Model
(在 Target Membership 中,主应用程序和小部件都被选中)。
当我在 Xcode 中启动(运行)小部件时,它会显示Event
已保存在主机应用程序中的所有应用程序事件 ()。但是,当我添加一个新事件时,它会出现在主机应用程序中,但不会出现在今天的小部件中。如果我重新启动小部件,则会显示所有事件,包括以前没有的最后一个事件。
这是获取事件的方法。它TodayViewController
在小部件中定义。
private func fetchEvents(date: Date) {
let predicates = NSCompoundPredicate(andPredicateWithSubpredicates: [
NSPredicate(format: "date = %@",Date().startOfDay as CVarArg),
NSPredicate(format: "startTime >= %@", Date() as CVarArg)
])
if let ev = try? TPEvent.fetchAll(predicates: predicates, in: persistentManager.context) {
events = ev
}
}
viewWillAppear
此事件在和中调用widgetPerformUpdate
。
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
fetchEvents(date: Date())
self.tableView.reloadData()
}
func widgetPerformUpdate(completionHandler: (@escaping (NCUpdateResult) -> Void)) {
self.fetchEvents(date: Date() )
self.tableView.reloadData()
completionHandler(NCUpdateResult.newData)
}
persistentManaged.context
是PersistentManager.shared.context
(见下面的代码)。
顺便说一句,当我查看 today-widget 时,会调用上述两种方法。我有很多时间来解决这个问题,但无法这样做。
可能是什么问题以及如何解决?
如果您需要更多信息或有任何问题,请发表评论。
更新
我有一个单身人士 PersistentManager
。viewContext
在主机应用程序和小部件中使用。
public final class PersistentManager {
init() {}
public static let shared = PersistentManager()
public lazy var persistentContainer: NSPersistentContainer = {
let container = NSPersistentCloudKitContainer(name: "Event")
guard let fileContainer = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.event.data") else {
fatalError("Shared file container could not be created.")
}
let storeURL = fileContainer.appendingPathComponent("Event.sqlite")
let storeDescription = NSPersistentStoreDescription(url: storeURL)
container.persistentStoreDescriptions = [storeDescription]
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
container.viewContext.automaticallyMergesChangesFromParent = true
do {
try container.viewContext.setQueryGenerationFrom(.current)
} catch {
fatalError("###\(#function): Failed to pin viewContext to the current generation:\(error)")
}
return container
}()
public lazy var context = persistentContainer.viewContext
// MARK: - Core Data Saving support
public func saveContext () {
let context = persistentContainer.viewContext
if context.hasChanges {
do {
try context.save()
} catch {
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}
}
解决方案
问题是主应用程序和应用程序扩展在 iOS 上作为两个不同的进程工作。
CoreDataNotificationCenter
仅在主应用程序进程中发送通知。因此,您必须在此处发送进程间通知。
在 iOS 上发送进程间通知的一种隐藏UserDefaults
方式是在对象上使用 KVO。
在NSUserDefaults.h
头文件中,Apple 声明
/*!NSUserDefaultsDidChangeNotification 会在当前进程中任何用户默认值更改时发布,但在普遍存在的默认值更改或外部进程更改默认值时不会发布。使用键值观察为感兴趣的特定键注册观察者将通知您所有更新,无论它们来自何处。*/
指定这一点后,可以假设通过在 的特定键上使用 KVO UserDefaults
,值更改将从应用程序传播到扩展程序,反之亦然。
因此,该方法可以是在主应用程序中的每次更改时,您将更改的当前时间戳保存到UserDefaults
:
/// When the change is made in the main app:
let defaults = UserDefaults(suiteName: "group.<your bundle id>")
defaults["LastChangeTimestamp"] = Date()
defaults.synchronize()
在应用程序扩展中:
let defaults = UserDefaults(suiteName: "group.<your bundle id>")
func subscribeForChangesObservation() {
defaults?.addObserver(self, forKeyPath: "LastChangeTimestamp", options: [.new, .initial], context: nil)
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
// Process your changes here.
}
deinit {
defaults?.removeObserver(self, forKeyPath: "LastChangeTimestamp")
}
推荐阅读
- objective-c - macOS 上的动画 NSView 约束(图层支持视图)objective-c
- docker - 使用`docker run -- rm`时有没有办法找到容器的运行时间
- python - 居中多行图例标题
- c# - 使用 Ban Unknown Properties 针对模式验证 JSON
- r - 抓取在线地图
- android - Android Jetpack Navigations:从“非导航”片段导航
- c# - 如何从骰子脚本中获取骰子值到石头脚本
- django - Django Modelforms:如何显示 CharField 的选择?
- java - 如何解决 DTO 的 modelMapper 错误?
- javascript - 向 Nativescript 元素添加伪选择器