[英]How do I refresh my SwiftUI view when my core data store has been updated from a background extension?
I have a SwiftUI app with an intents extension target.我有一个带有意图扩展目标的 SwiftUI 应用程序。 I also have a core data store located on a shared app group.我还有一个位于共享应用程序组的核心数据存储。
The intents extension can successfully write data to the store using newBackgroundContext()
in the Shortcuts app. Intents 扩展可以使用 Shortcuts 应用程序中的newBackgroundContext()
成功地将数据写入存储。 My SwiftUI app can read the data from the app group in viewContext
but only when I force quit the app and re-open it.我的viewContext
应用程序可以从 viewContext 中的应用程序组读取数据,但只有当我强制退出应用程序并重新打开它时。
I think I need a way to let my app know that it has been updated in a different context but I'm not sure how do do that?我想我需要一种方法让我的应用知道它已在不同的上下文中更新,但我不确定如何做到这一点?
I've tried setting context.automaticallyMergesChangesFromParent = true
in the app delegate and adding context.refreshAllObjects()
to onAppear
in the SwiftUI view but that doesn't seem to make any difference.我尝试在应用程序委托中设置context.automaticallyMergesChangesFromParent = true
并在 SwiftUI 视图中将context.refreshAllObjects()
添加到onAppear
,但这似乎没有任何区别。
mergeChanges(fromContextDidSave:) seems promising but I'm not sure how to get the Notification. mergeChanges(fromContextDidSave:)看起来很有希望,但我不确定如何获得通知。
I'm new to Core Data and very confused, so any pointers in the right direction would be great, thanks!我是 Core Data 的新手并且非常困惑,所以任何正确方向的指针都会很棒,谢谢!
Here's a sanitised version of my current code:这是我当前代码的净化版本:
App Delegate应用代理
lazy var persistentContainer: NSCustomPersistentContainer = {
let container = NSCustomPersistentContainer(name: "exampleContainerName")
container.loadPersistentStores { description, error in
if let error = error {
print("error loading persistent core data store")
}
}
return container
}()
func saveContext () {
let context = persistentContainer.viewContext
if context.hasChanges {
do {
try context.save()
} catch {
print("error saving core data context")
}
}
}
class NSCustomPersistentContainer: NSPersistentContainer {
override open class func defaultDirectoryURL() -> URL {
var storeURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "exampleAppGroupName")
storeURL = storeURL?.appendingPathComponent("exampleContainerName")
return storeURL!
}
}
Scene Delegate场景代表
guard let context = (UIApplication.shared.delegate as? AppDelegate)?.persistentContainer.viewContext else {
fatalError("Unable to read managed object context.")
}
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: ExampleView.environment(\.managedObjectContext, context))
self.window = window
window.makeKeyAndVisible()
}
Swift UI View Swift 用户界面视图
import SwiftUI
import CoreData
struct ExampleView: View {
@Environment(\.managedObjectContext) var managedObjectContext
@FetchRequest(
entity: ExampleEntityName.entity(),
sortDescriptors: [
NSSortDescriptor(keyPath: \ExampleEntityName.date, ascending: false),
]
) var items: FetchedResults<ExampleEntityName>
var body: some View {
VStack {
List(items, id: \.self) { item in
Text("\(item.name)")
}
}
}
}
Intent Extension意图扩展
class NSCustomPersistentContainer: NSPersistentContainer {
override open class func defaultDirectoryURL() -> URL {
var storeURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "exampleContainerName")
storeURL = storeURL?.appendingPathComponent("exampleAppGroupName")
return storeURL!
}
}
let persistentContainer: NSCustomPersistentContainer = {
let container = NSCustomPersistentContainer(name: "exampleContainerName")
container.loadPersistentStores { description, error in
if let error = error {
print("error loading persistent core data store: \(error)")
}
}
return container
}()
let context = persistentContainer.newBackgroundContext()
let entity = NSEntityDescription.entity(forEntityName: "ExampleEntityName", in: context)
let newEntry = NSManagedObject(entity: entity!, insertInto: context)
newEntry.setValue(Date(), forKey: "date")
newEntry.setValue("Hello World", forKey: "name")
do {
try context.save()
} catch {
print("Failed saving")
}
Don't know if you discovered an answer yet, but I had a similar problem using UserDefaults and an intent extension in a shared app group.不知道您是否找到了答案,但我在共享应用程序组中使用 UserDefaults 和意图扩展时遇到了类似的问题。 The thing that finally worked for me was adding a call to load the items in the method below in the SceneDelegate:最终对我有用的事情是在 SceneDelegate 的以下方法中添加调用以加载项目:
func sceneWillEnterForeground(_ scene: UIScene) {
// Called as the scene transitions from the background to the foreground.
// Use this method to undo the changes made on entering the background.
SiriResponseManager.shared.grabUD()
}
In my example I am using a singleton that houses the method to load the items from the UserDefaults.在我的示例中,我使用的是 singleton,其中包含从 UserDefaults 加载项目的方法。 This singleton class is an ObservableObject.这个 singleton class 是一个 ObservableObject。 In my first view that loads (ContentView in my case) I assign an ObservedObject variable to my singleton class and retrieve the @State variables in my singleton to populate the string in my view.在我加载的第一个视图中(在我的情况下为 ContentView),我将 ObservedObject 变量分配给我的 singleton class 并检索我的 singleton 中的 @State 变量以填充我的视图中的字符串
Hopefully this helps.希望这会有所帮助。
class Data: ObservableObject {
@Published var dataToUpdate: Int
}
struct UpdateView : View {
@EnvironmentObject data: Data
...body ... {
// use data.dataToUpdate here
}
}
Just define data like you see in this example.只需像您在此示例中看到的那样定义数据。 Then fill dataToUpdate whenever your database changes...of course this is just a simplified example with just an Int Value...然后在您的数据库更改时填充 dataToUpdate ......当然这只是一个简单的例子,只有一个 Int 值......
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.