首页 > 解决方案 > 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.contextPersistentManager.shared.context(见下面的代码)。

顺便说一句,当我查看 today-widget 时,会调用上述两种方法。我有很多时间来解决这个问题,但无法这样做。

可能是什么问题以及如何解决?

如果您需要更多信息或有任何问题,请发表评论。

更新

我有一个单身人士 PersistentManagerviewContext在主机应用程序和小部件中使用。

 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)")
            }
        }
    }
}

标签: iosswiftcore-datatoday-extension

解决方案


问题是主应用程序和应用程序扩展在 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")
}

推荐阅读