SwiftData Update Item View from Background Thread

I have a background thread that is updating a swift data model Item using a ModelActor. The background thread runs processing an Item and updates the Item's status field. I notice that if I have a view like

struct ItemListView: View {
    @Query private var items: [Items]

    var body: some View {
        VStack {
            ForEach(items) { item in
                ItemDetailView(item)
            }
        }
    }
}

struct ItemDetailView: View {
    var item: Item
    var body: some View {
        // expected: item.status automatically updates when the background thread updates the `Item`'s `status`.
        Text(item.status)
        // actual: This text never changes
    }
}

Then background updates to the Item's status in SwiftData does not reflect in the ItemDetailView. However, if I inline ItemDetailView in ItemListView like this:

struct ItemListView: View {
    @Query private var items: [Items]

    var body: some View {
        VStack {
            ForEach(items) { item in
                // Put the contents of ItemDetailView directly in ItemListView
                Text(item.status)
                // result: item.status correctly updates when the background thread updates the item.
            }
        }
    }
}

Then the item's status text updates in the UI as expected. I suspect ItemDetailView does not properly update the UI because it just takes an Item as an input. ItemDetailView would need additional understanding of SwiftData, such as a ModelContext.

Is there a way I can use ItemDetailView to show the Item's status and have the UI show the status as updated in the background thread?

In case details about my background thread helps solve the problem, my thread is invoked from another view's controller like

@Observable
class ItemCreateController {
    func queueProcessingTask() {
        Task {
            let itemActor = ItemActor(modelContainer: modelContainer)
            await itemActor.setItem(item)
            await itemActor.process()
        }
    }
}

@ModelActor
actor ItemActor {
    var item: Item?
    func setItem(_ item: Item) {
        self.item = modelContext.model(for: item.id) as? Item
    }
    func process() async {
        // task that runs processing on the Item and updates the Item's status as it goes.
    }

Quick question, are you on iOS 18? Did it work in iOS 17?

I'm trying to figure out a similar issue that worked in iOS 17, but doesn't work in iOS 18 Beta 1 and Beta 2 (just updated today).

In a nutshell I have a parent and child view as part of a NavigationSplitView.

A parent view has a @Query for items, the item is passed to the child view, like in your case. In the child there is a method that performs an async task, which updates the item in the background using @ModelActor

This update was immediately refrelcted in the child view on iOS 17 but is not reflected in iOS 18. Only if I navigate to parent view and then re-open child I can see the changes.

I'm having a similar issue, where a query-driven parent passes results to another view. Updates are not reflected until leaving the view and returning to it.

I've confirmed that updates made in a background context are not being reflected in the query results. If I keep everything on the main actor it is fine. Certainly a bug in iOS 18 b1 and b2.

Still broken in beta 3

Beta 4, still broken

Beta 5 still broken.

I have the same issue. Very disappointing of Apple

SwiftData Update Item View from Background Thread
 
 
Q