【问题标题】:iOS - Notify today extension for Core Data changes in the main appiOS - 通知今天扩展主应用程序中的核心数据更改
【发布时间】:2020-02-06 21:49:54
【问题描述】:

我有一个名为 EventNSManagedObject 在主机应用程序和今天的扩展程序之间共享。 (在 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
    }
} 

此事件在viewWillAppearwidgetPerformUpdate 中调用。

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 时,上面的两个方法都会被调用。我有很多时间来解决这个问题,但无法这样做。

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

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

更新

我有一个单身人士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)")
            }
        }
    }
}

【问题讨论】:

  • 1-检查是否在添加新事件时明确地对 parentContext 执行保存。 2-检查您是否将 viewContext 设置为获取最新更改:viewContext.automaticallyMergesChangesFromParent = YES
  • @Idali 我添加了更多代码。如您所见,viewContext.automaticallyMergesChangesFromParent true。我还节省了viewContext
  • 你能解决这个问题吗?
  • @fphelp 你知道了吗?我还在寻找解决方案
  • @mahan no :(((如果你发现了什么,请在这里评论!

标签: ios swift core-data today-extension


【解决方案1】:

问题是主应用和应用扩展在 iOS 上作为两个不同的进程工作。

CoreData 与NotificationCenter 一起使用,它只在主应用程序进程中发送通知。因此,您必须在此处发送进程间通知。

在 iOS 上发送进程间通知的一种隐藏方法是在 UserDefaults 对象上使用 KVO。

NSUserDefaults.h 头文件中苹果声明

/*! NSUserDefaultsDidChangeNotification 会在当前进程中任何用户默认值更改时发布,但在普遍存在的默认值更改或外部进程更改默认值时不会发布。使用键值观察为感兴趣的特定键注册观察者将通知您所有更新,无论它们来自何处。 */

如果指定了这一点,可以假设通过在 UserDefaults 的特定键上使用 KVO,值更改将从应用程序传播到扩展程序,反之亦然。

因此,方法可以是在主应用程序中的每次更改时,将更改的当前时间戳保存到 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")
}

【讨论】:

  • 感谢您的回答。但是,您的回答根本没有帮助。这是一个关于 CoreData 的问题,而不是用户默认值。
  • 我在回答前加了一个解包文字,以便您解决问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-04
  • 2017-07-10
  • 2017-05-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多