I'm working on a SwiftUI app using SwiftData for state management. I've encountered an issue with view updates when mutating a model. Here's a minimal example to illustrate the problem:
import SwiftUI
import SwiftData
// MARK: - Models
@Model
class A {
@Relationship var bs: [B] = [B]()
init(bs: [B]) {
self.bs = bs
}
}
@Model
class B {
@Relationship var cs: [C] = [C]()
init(cs: [C]) {
self.cs = cs
}
}
@Model
class C {
var done: Bool
init(done: Bool) {
self.done = done
}
}
// MARK: - Views
struct CView: View {
var c: C
var body: some View {
@Bindable var c = c
HStack {
Toggle(isOn: $c.done, label: {
Text("Done")
})
}
}
}
struct BView: View {
var b: B
var body: some View {
List(b.cs) { c in
CView(c: c)
}
}
}
struct AView: View {
var a: A
var body: some View {
List(a.bs) { b in
NavigationLink {
BView(b: b)
} label: {
Text("B \(b.cs.allSatisfy({ $0.done }).description)")
}
}
}
}
struct ContentView: View {
@Query private var aList: [A]
var body: some View {
NavigationStack {
List(aList) { a in
NavigationLink {
AView(a: a)
} label: {
Text("A \(a.bs.allSatisfy({ $0.cs.allSatisfy({ $0.done }) }).description)")
}
}
}
}
}
@main
struct Minimal: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
#Preview {
let config = ModelConfiguration(isStoredInMemoryOnly: true)
let container = try! ModelContainer(for: A.self, configurations: config)
var c = C(done: false)
container.mainContext.insert(c)
var b = B(cs: [c])
container.mainContext.insert(b)
var a = A(bs: [b])
container.mainContext.insert(a)
return ContentView()
.modelContainer(container)
}
In this setup, I have a CView where I toggle the state of a model C. After toggling C and navigating back, the grandparent view AView does not reflect the updated state (it still shows false instead of true). However, if I navigate back to the root ContentView and then go to AView, the status is updated correctly.
Why doesn't AView update immediately after mutating C in CView, but updates correctly when navigating back to the root ContentView? I expected the grandparent view to reflect the changes immediately as per SwiftData's observation mechanism.