SwiftUI中相关实体更改时,如何更新@FetchRequest? [英] How to update @FetchRequest, when a related Entity changes in SwiftUI?
问题描述
在SwiftUI 视图
中,我有一个 List
基于 @FetchRequest
显示主要
实体的数据以及通过 Secondary
实体连接的过孔关系的数据。
当我添加新的时,
实体。视图
及其列表
已正确更新。具有新的相关辅助实体的主要
In a SwiftUI View
i have a List
based on @FetchRequest
showing data of a Primary
entity and the via relationship connected Secondary
entity.
The View
and its List
is updated correctly, when I add a new Primary
entity with a new related secondary entity.
问题是,当我更新连接的次要实体$时详细信息视图中的c $ c>项,数据库将被更新,但更改未反映在
主要
列表中。
显然, @FetchRequest
不会被另一个视图中的更改触发。
The problem is, when I update the connected Secondary
item in a detail view, the database gets updated, but the changes are not reflected in the Primary
List.
Obviously, the @FetchRequest
does not get triggered by the changes in another View.
当我此后,在主视图中添加新项,最后更改的项将最终更新。
When I add a new item in the primary view thereafter, the previously changed item gets finally updated.
作为一种解决方法,我还更新了的属性详细信息视图中的主要
实体,更改将正确传播到主要
视图。
As a workaround, i additionally update an attribute of the Primary
entity in the detail view and the changes propagate correctly to the Primary
View.
我的问题是:
如何在SwiftUI核心数据中强制对所有相关的 @FetchRequests
进行更新?
特别是当我无权直接访问相关实体/ @Fetchrequests
?
My question is:
How can I force an update on all related @FetchRequests
in SwiftUI Core Data?
Especially, when I have no direct access to the related entities/@Fetchrequests
?
import SwiftUI
extension Primary: Identifiable {}
// Primary View
struct PrimaryListView: View {
@Environment(\.managedObjectContext) var context
@FetchRequest(
entity: Primary.entity(),
sortDescriptors: [NSSortDescriptor(key: "primaryName", ascending: true)]
)
var fetchedResults: FetchedResults<Primary>
var body: some View {
List {
ForEach(fetchedResults) { primary in
NavigationLink(destination: SecondaryView(primary: primary)) {
VStack(alignment: .leading) {
Text("\(primary.primaryName ?? "nil")")
Text("\(primary.secondary?.secondaryName ?? "nil")").font(.footnote).foregroundColor(.secondary)
}
}
}
}
.navigationBarTitle("Primary List")
.navigationBarItems(trailing:
Button(action: {self.addNewPrimary()} ) {
Image(systemName: "plus")
}
)
}
private func addNewPrimary() {
let newPrimary = Primary(context: context)
newPrimary.primaryName = "Primary created at \(Date())"
let newSecondary = Secondary(context: context)
newSecondary.secondaryName = "Secondary built at \(Date())"
newPrimary.secondary = newSecondary
try? context.save()
}
}
struct PrimaryListView_Previews: PreviewProvider {
static var previews: some View {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
return NavigationView {
PrimaryListView().environment(\.managedObjectContext, context)
}
}
}
// Detail View
struct SecondaryView: View {
@Environment(\.presentationMode) var presentationMode
var primary: Primary
@State private var newSecondaryName = ""
var body: some View {
VStack {
TextField("Secondary name:", text: $newSecondaryName)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
.onAppear {self.newSecondaryName = self.primary.secondary?.secondaryName ?? "no name"}
Button(action: {self.saveChanges()}) {
Text("Save")
}
.padding()
}
}
private func saveChanges() {
primary.secondary?.secondaryName = newSecondaryName
// TODO: ❌ workaround to trigger update on primary @FetchRequest
primary.managedObjectContext.refresh(primary, mergeChanges: true)
// primary.primaryName = primary.primaryName
try? primary.managedObjectContext?.save()
presentationMode.wrappedValue.dismiss()
}
}
推荐答案
您需要一个发布者,该发布者将生成有关上下文更改的事件以及主视图中的一些状态变量,以强制从该发布者接收事件时重建视图。
重要说明:视图构建器代码中必须使用状态变量 ,否则渲染引擎将不知道某些更改。
You need a Publisher which would generate event about changes in context and some state variable in primary view to force view rebuild on receive event from that publisher.
Important: state variable must be used in view builder code, otherwise rendering engine would not know that something changed.
这里是对代码受影响部分的简单修改,即可提供所需的行为。
Here is simple modification of affected part of your code, that gives behaviour that you need.
@State private var refreshing = false
private var didSave = NotificationCenter.default.publisher(for: .NSManagedObjectContextDidSave)
var body: some View {
List {
ForEach(fetchedResults) { primary in
NavigationLink(destination: SecondaryView(primary: primary)) {
VStack(alignment: .leading) {
// below use of .refreshing is just as demo,
// it can be use for anything
Text("\(primary.primaryName ?? "nil")" + (self.refreshing ? "" : ""))
Text("\(primary.secondary?.secondaryName ?? "nil")").font(.footnote).foregroundColor(.secondary)
}
}
// here is the listener for published context event
.onReceive(self.didSave) { _ in
self.refreshing.toggle()
}
}
}
.navigationBarTitle("Primary List")
.navigationBarItems(trailing:
Button(action: {self.addNewPrimary()} ) {
Image(systemName: "plus")
}
)
}
这篇关于SwiftUI中相关实体更改时,如何更新@FetchRequest?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!