SwiftUI not observing SwiftData changes

I have an app with the following model:

@Model class TaskList {
    @Attribute(.unique)
    var name: String
    
    // Relationships
    var parentList: TaskList?
    
    @Relationship(deleteRule: .cascade, inverse: \TaskList.parentList)
    var taskLists: [TaskList]?
        
    init(name: String, parentTaskList: TaskList? = nil) {
        self.name = name
        self.parentList = parentTaskList
        self.taskLists = []
    }
}

If I run the following test, I get the expected results - Parent has it's taskLists array updated to include the Child list created. I don't explicitly add the child to the parent array - the parentList relationship property on the child causes SwiftData to automatically perform the append into the parent array:

    @Test("TaskList with children with independent saves are in the database")
    func test_savingRootTaskIndependentOfChildren_SavesAllTaskLists() async throws {
        let modelContext = TestHelperUtility.createModelContext(useInMemory: false)
        let parentList = TaskList(name: "Parent")
        
        modelContext.insert(parentList)
        try modelContext.save()
        
        let childList = TaskList(name: "Child")
        childList.parentList = parentList
        modelContext.insert(childList)
        try modelContext.save()
        
        let fetchedResults = try modelContext.fetch(FetchDescriptor<TaskList>())
        let fetchedParent = fetchedResults.first(where: { $0.name == "Parent"})
        let fetchedChild = fetchedResults.first(where: { $0.name == "Child" })
        #expect(fetchedResults.count == 2)
        #expect(fetchedParent?.taskLists.count == 1)
        #expect(fetchedChild?.parentList?.name == "Parent")
        #expect(fetchedChild?.parentList?.taskLists.count == 1)
    }

I have a subsequent test that deletes the child and shows the parent array being updated accordingly.

With this context in mind, I'm not seeing these relationship updates being observed within SwiftUI. This is an app that reproduces the issue. In this example, I am trying to move "Finance" from under the "Work" parent and into the "Home" list.

I have a List that loops through a @Query var taskList: [TaskList] array. It creates a series of children views and passes the current TaskList element down into the view as a binding.

When I perform the operation below the "Finance" element is removed from the "Work" item's taskLists array automatically and the view updates to show the removal within the List. In addition to that, the "Home" item also shows "Finance" within it's taskLists array - showing me that SwiftData is acting how it is supposed to - removed the record from one array and added it to the other.

The View does not reflect this however. While the view does update and show "Finance" being removed from the "Work" list, it does not show the item being added to the "Home" list. If I kill the app and relaunch I can then see the "Finance" list within the "Home" list. From looking at the data in the debugger and in the database, I've confirmed that SwiftData is working as intended. SwiftUI however does not seem to observe the change.

ToolbarItem {
    Button("Save") {
        list.name = viewModel.name
        list.parentList = viewModel.parentTaskList
        
        try! modelContext.save()
        dismiss()
    }
}

To troubleshoot this, I modified the above code so that I explicitly add the "Finance" list to the "Home" items taskLists array.

ToolbarItem {
    Button("Save") {
        list.name = viewModel.name
        
        list.parentList = viewModel.parentTaskList
        
        if let newParent = viewModel.parentTaskList {
            // MARK: Bug - This resolves relationship not being reflected in the View
            newParent.taskLists?.append(list)
        }
        
        try! modelContext.save()
        dismiss()
    }
}

Why does my explicit append call solve for this? My original approach (not manually updating the arrays) works fine in every unit/integration test I run but I can't get SwiftUI to observe the array changes.

Even more strange is that when I look at viewModel.parentTaskList.taskLists in this context, I can see that the list item already exists in it. So my code effectively tries to add it a second time, which SwiftData is smart enough to prevent from happening. When I do this though, SwiftUI observes a change in the array and the UI reflects the desired state.

In addition to this, if I replace my custom list rows with an OutlineGroup this issue doesn't manifest itself. SwiftUI stays updated to match SwiftData when I remove my explicit array addition.

I don't understand why my views, which is passing the TaskList all the way down the stack via Bindable is not updating while an OutlineGroup does.

I have a complete reproducible ContentView file that demonstrates this as a Gist. I tried to provide the source here but it was to much for the post.

One other anecdote. When I navigate to the TaskListEditorScreen and open the TaskListPickerScreen I get the following series of errors:

error: the replacement path doesn't exist: "/var/folders/07/3px_03md30v9n105yh3rqzvw0000gn/T/swift-generated-sources/@_swiftmacro_09SwiftDataA22UIChangeDetectionIssue20TaskListPickerScreenV9taskLists33_A40669FFFCF66BB4EEA5302BB5ED59CELL5QueryfMa.swift"

I saw another post regarding these and I'm wondering if my issue is related to this.

So my question is, do I need to handle observation of SwiftData models containing arrays differently in my custom views? Why do bindings not observe changes made by SwiftData but they observe changes made explicitly by me?

I still haven't figured out why this is happening. I'm moving forward with manually updating the arrays but would still like to understand if the SwiftData changes to the array are supposed to be picked up by SwiftUI. If so, then I can file a bug but I'm not sure how to tell if this is expected behavior or not.

SwiftUI not observing SwiftData changes
 
 
Q