iOS-立即通知主应用中核心数据更改的扩展 [英] iOS - Notify today extension for Core Data changes in the main app

查看:69
本文介绍了iOS-立即通知主应用中核心数据更改的扩展的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个名为 Event NSManagedObject ,它在主机应用程序和今天的扩展程序之间共享。 (在目标成员身份中,同时选中了主应用程序和小部件)。

I have a NSManagedObject called Event that is shared between the host app and today extension. (In Target Membership, both the main app and the widget are checked).

主机应用和小部件具有相同的应用组标识符,并且都共享数据模型(在目标会员身份中,两者

The host app and widget have the same App Group identifier and both share Data Model(In Target Membership, both the main app and the widget are checked).

当我在Xcode中启动(运行)小部件时,它将显示所有应用程序事件(事件)已保存在主机应用中。但是,当我添加一个新事件时,它会出现在主机应用程序中,但不会出现在Today-widget中。如果我重新启动窗口小部件,则会显示所有事件,包括以前不是的最后一个事件。

When I launch(run) the widget in Xcode, it shows all of the app events (Event) that are already saved in the host app. However, when I add a new event, it appears in the host app but NOT in today-widget. If I relaunch the widget, all the events are shown including the last event that previously was not.

这是获取事件的方法。它在小部件的 TodayViewController 中定义。

This is the method that fetches events. It is defined in TodayViewController of the widget.

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 (请参见下面的代码)。

persistentManaged.context is PersistentManager.shared.context (see code below).

顺便说一句,当我查看Today-widget时,上述两种方法都被调用。我有很多时间弄清楚这个问题,但无法解决。

By the way, both of the methods above are called when I view today-widget. I have a lot of time figuring out this issue but could not do so.

请仅在需要时发表评论更多信息或有任何疑问。

我有一个单身人士 PersistentManager 。在主机应用和小部件中都使用 viewContext

I have a singleton PersistentManager. Use viewContext both in the host app and widget.

 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上作为两个不同的进程工作。

The issue is that the main app and the app extension work as two different processes on iOS.

CoreData与 NotificationCenter 一起使用,后者仅在主应用程序进程内发送通知。因此,您必须在此处发送进程间通知。

CoreData works with NotificationCenter which sends notifications only within the main app process. Thus, you have to send interprocess notification here.

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

One hidden way to send interprocess notification on iOS is to use KVO on the UserDefaults object.

NSUserDefaults.h 头文件中,苹果指出


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

/*! NSUserDefaultsDidChangeNotification is posted whenever any user defaults changed within the current process, but is not posted when ubiquitous defaults change, or when an outside process changes defaults. Using key-value observing to register observers for the specific keys of interest will inform you of all updates, regardless of where they're from. */

指定了此选项后,就可以假定通过对 UserDefaults ,值更改将从应用程序传播到扩展程序,反之亦然。

Having this specified, one can assume that by using KVO on the particular key of UserDefaults, the value change will be propagated from the app to the extension, and vice versa.

因此,方法可以是主应用程序中的每个更改都会将更改的当前时间戳保存到 UserDefaults

So, the approach can be that on each change in the main app you save the current timestamp of the change into the 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")
}

这篇关于iOS-立即通知主应用中核心数据更改的扩展的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆