I'm having an issue where FetchRequest does not consistently reflect changes that are made in the CoreData model. Things seem to work fine if you create or delete any object before editing, but if you only edit an object, the changes will not be shown.
Here is a minimal repro: https://github.com/literalpie/fetchrequest-bug/tree/main
I have a workaround that involved adding a "noop" predicate that gets toggled whenever objectWillChange
is emitted. This seems to force the FetchRequest to re-look at things.
.onReceive(items.publisher.flatMap(\.objectWillChange), perform: { _ in
items.nsPredicate = update ? NSPredicate(value: true) : NSPredicate(format: "1 == 1")
update.toggle()
})
ForEach(items)
uses the item
identifier to identify the destination view. When you change the item
name, the item
identifier doesn't change, and so SwiftUI doesn't guarantee to update the destination view, which explains why the destination view doesn't show the latest change.
You can fix the issue by converting the destination view to a separate custom view, and in the custom view, annotate the item with @ObservedObject
which tells SwiftUI to observe the properties of the item
(and update the view for any property change), as shown below:
struct EditView: View {
@ObservedObject var item: Item
var body: some View {
VStack {
Text("Item \(item.name ?? "unnamed") at \(item.timestamp!, formatter: itemFormatter)")
Button("Add '?'") {
item.name = (item.name ?? "") + "?"
try? PersistenceController.shared.container.viewContext.save()
}
}
}
}
struct ContentView: View {
...
NavigationLink {
EditView(item: item)
} label: {
...
}
Best,
——
Ziqiao Chen
Worldwide Developer Relations.