I seem to have come up with a workaround. Placing a View between the View containing the NavigationStack and the one containing the @Query with predicate filter appears to solve the problem. The view graph no longer gets invalidated when clicking to navigate away or back.
The resulting code looks like this:
struct ContentView: View {
var body: some View {
NavigationStack {
let _ = Self._printChanges()
MiddleView()
.navigationDestination(for: Item.self) { item in
Text("Item at \(item.num)")
}
}
}
}
struct MiddleView: View {
var body: some View {
let _ = Self._printChanges()
SubView()
}
}
struct SubView: View {
@Environment(\.modelContext) private var modelContext
@Query(filter: #Predicate<Item> { item in
item.num < 20
}, sort: \.num) private var items: [Item]
var body: some View {
let _ = Self._printChanges()
List {
ForEach(items) { item in
NavigationLink(value: item) {
Text("Item \(item.num)")
}.background(Color.random())
}
}
}
}
Not only that, but it appears that using NavigationLink(destination:, label: ) in the SubView seems to work now as well whereas before it would sometimes cause an infinite loop when navigating from a view with a Query predicate to another view with one.
Post
Replies
Boosts
Views
Activity
I ran into a similar problem. It seems if a NavigationStack is placed in the parent view of a child view with a @Query with predicate, the child view will get invalidated when navigating to or back from the destination view. It seems if the destination View also references the model, that too will get invalidated leading to it navigating back before trying to navigate to the new item leading to a loop.
I managed to work around it a couple ways, but the simplest is adding a dummy intermediate View between the View with the NavigationStack and the one with the @Query with predicate like this:
struct ContentView: View {
var body: some View {
NavigationStack {
DummyView()
}
}
}
struct DummyView: View {
var body: some View {
ListsView()
}
}
struct ListsView: View {
@Environment(\.modelContext) private var modelContext
@Query(filter: #Predicate<Item> { _ in true })
// ...
}
let notification = Notification(newDateId)
note.notification = notification
I believe you need to call modelContext.insert (and possibly save) on the new Notification before you set it as note.notification.
Filing a bug report with Apple seems to have fixed my issue.
I filed a bug FB15592048 for it.
I've submitted this as FB16292376
Since order of database records appears to be an issue, sticking a context.save() after inserting the root node fixes the problem.
let root = TreeNode()
context.insert(root)
try context.save()
for _ in 0..<100 {
...
However, this workaround isn't usable if there are mutations. For instance, creating a new branch node and moving all the root.children to it would lead to the same problem. So would creating a new parent node of root later.
Internally this appears to be caused by this SQLite trigger created by Core Data:
CREATE TEMPORARY TRIGGER IF NOT EXISTS ZQ_ZTREENODE_TRIGGER AFTER DELETE ON ZTREENODE FOR EACH ROW
BEGIN
DELETE FROM ZTREENODE WHERE ZPARENT = OLD.Z_PK;
SELECT RAISE(FAIL, 'Batch delete failed due to mandatory OTO nullify inverse on TreeNode/parent') FROM ZTREENODE WHERE Z_PK = OLD.ZPARENT;
END
If the deletes happened such that parents were always deleted before children, this would work. Unfortunately that's not the case.
I've submitted the crash with optional reverse relationships as a second bug FB16292988