Swift 从主应用程序或扩展程序更改后刷新核心数据

Swift 从主应用程序或扩展程序更改后刷新核心数据,swift,core-data,nsfetchedresultscontroller,today-extension,ios-app-group,Swift,Core Data,Nsfetchedresultscontroller,Today Extension,Ios App Group,我一直在为我的核心数据应用程序开发“今日”小部件。我对小部件和主应用程序使用nsfetchedresultscoontroller,并在使用UserDefaults和此应用程序的帮助从相反目标更改/添加数据时收到通知 但是更新数据或NSFetchedResultsController以查看更改/添加不起作用。我试过: 从NSFetchedResultsController重新读取数据 将持久化容器的视图上下文stalenessInterval设置为0,并调用viewContext.refres

我一直在为我的核心数据应用程序开发“今日”小部件。我对小部件和主应用程序使用
nsfetchedresultscoontroller
,并在使用UserDefaults和此应用程序的帮助从相反目标更改/添加数据时收到通知

但是更新数据或NSFetchedResultsController以查看更改/添加不起作用。我试过:

  • 从NSFetchedResultsController重新读取数据
  • 将持久化容器的视图上下文
    stalenessInterval
    设置为0,并调用
    viewContext.refreshAllObjects()
    ,然后尝试重新提取数据(或不重新提取)
  • 在fetchedController上将
    设置为true时,应将RefreshRefetchedObjects设置为true,以便自动调用
我知道数据正在保存,因为如果我强制退出应用程序或重新运行小部件,新数据就在那里。但我不知道当相反的东西发生了变化时,如何刷新应用程序或小部件

在过去的几天里,我一直在寻找一个解决方案,这是我需要用这个小部件做的最后一件事。如果有人知道怎么做,请帮忙

如何设置我的
NSFetchedResultsController

lazy var fetchedResultsController: NSFetchedResultsController<Item> = setupFetchedController()

override func viewDidAppear(_ animated: Bool) {
        loadData()

        //check to see if any data was changed. Being notified about changes works every time
        subscribeForChangesObservation()
}


private func setupFetchedController() -> NSFetchedResultsController<Item> {
        let managedContext = CoreDataManager.sharedManager.persistentContainer.viewContext

        let sortDescriptor = NSSortDescriptor(key: "date", ascending: true)
        let request : NSFetchRequest<Item> = Item.fetchRequest()
        request.predicate = NSPredicate(format: "date <= %@", Date() as NSDate)
        request.sortDescriptors = [sortDescriptor]

        fetchedResultsController = NSFetchedResultsController(fetchRequest: request, managedObjectContext: managedContext, sectionNameKeyPath: nil, cacheName: nil)
        fetchedResultsController.delegate = self

        return fetchedResultsController
}

private func loadData() {
        do {
            try fetchedResultsController.performFetch()
            updateSnapshot()
        } catch {
            print("Hey Listen! Error performing fetchedResultsController fetch: \(error)")
        }
}

//reloads the items in the table
func updateSnapshot() {
        let fetchedItems = fetchedResultsController.fetchedObjects ?? []
        var snapshot = NSDiffableDataSourceSnapshot<Int, Item>()
        snapshot.appendSections([0])
        snapshot.appendItems(fetchedItems)
        dataSource.apply(snapshot, animatingDifferences: true)
}
class CoreDataManager {
    static let sharedManager = CoreDataManager()
    private init() {}

    lazy var persistentContainer: NSPersistentContainer = {
        var useCloudSync = UserDefaults.standard.bool(forKey: "useCloudSync")

        //Get the correct container
        let containerToUse: NSPersistentContainer?
        if useCloudSync {
           //custom container to just set the defaultDirectoryURL to the app group url
           containerToUse = GroupedPersistentCloudKitContainer(name: "App")
        } else {
            containerToUse = NSPersistentContainer(name: "App")      
        }

        guard let container = containerToUse else {
            fatalError("Couldn't get a container")
        }

        //Set the storeDescription
        let storeURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.App")!.appendingPathComponent("\(container.name).sqlite")

        var defaultURL: URL?
        if let storeDescription = container.persistentStoreDescriptions.first, let url = storeDescription.url {
            defaultURL = FileManager.default.fileExists(atPath: url.path) ? url : nil
        }

        if defaultURL == nil {
            container.persistentStoreDescriptions = [NSPersistentStoreDescription(url: storeURL)]
        }


        let description = container.persistentStoreDescriptions.first else {
            fatalError("Hey Listen! ###\(#function): Failed to retrieve a persistent store description.")
        }

        description.setOption(true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey)
        if !useCloudSync {
            description.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey)
        }

        container.loadPersistentStores(completionHandler: { (storeDescription, error) in

            //migrate from old url to use app groups
            if let url = defaultURL, url.absoluteString != storeURL.absoluteString {
                let coordinator = container.persistentStoreCoordinator
                if let oldStore = coordinator.persistentStore(for: url) {
                    do {
                        try coordinator.migratePersistentStore(oldStore, to: storeURL, options: nil, withType: NSSQLiteStoreType)
                    } catch {
                        print("Hey Listen! Error migrating persistent store")
                        print(error.localizedDescription)
                    }

                    // delete old store
                    let fileCoordinator = NSFileCoordinator(filePresenter: nil)
                    fileCoordinator.coordinate(writingItemAt: url, options: .forDeleting, error: nil, byAccessor: { url in
                        do {
                            try FileManager.default.removeItem(at: url)
                        } catch {
                            print("Hey Listen! Error deleting old persistent store")
                            print(error.localizedDescription)
                        }
                    })
                }
            }
         }

        container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
        container.viewContext.transactionAuthor = appTransactionAuthorName

        // Pin the viewContext to the current generation token and set it to keep itself up to date with local changes.
        container.viewContext.automaticallyMergesChangesFromParent = true
        do {
            try container.viewContext.setQueryGenerationFrom(.current)
        } catch {
            fatalError("Hey Listen! ###\(#function): Failed to pin viewContext to the current generation:\(error)")
        }

        // Observe Core Data remote change notifications.
        NotificationCenter.default.addObserver(
            self, selector: #selector(type(of: self).storeRemoteChange(_:)),
            name: .NSPersistentStoreRemoteChange, object: container.persistentStoreCoordinator)

         return container
   }
}

您是否已将其包括在viewContext设置中

persistentContainer.viewContext.automaticallyMergesChangesFromParent = true

你能展示一下你是如何实现NSFetchedResultsController的吗?你是否使用了FRC的缓存?@rbaldwin刚刚添加了代码!如果你知道如何解决这个问题,请告诉我@pbasdf我不认为我在缓存,因为我在FRCI中保留了cacheName nil,以确保您正在使用查询生成。我对他们不是很熟悉,但你不需要让这一代人接受这些变化吗?谢谢你的回复!不幸的是,我已经在核心数据设置中设置了来自父级的
自动数据管理。我已将我的
persistentContainer
设置代码添加到我的问题中,以便您可以看到它。如果你知道如何解决这个问题,请告诉我!!我想不出来