I have an app with fairly typical requirements - I need to insert some data (in my case from the network but could be anything) and I want to do it in the background to keep the UI responsive.
I'm using SwiftData.
I've created a ModelActor that does the importing and using the debugger I can confirm that the data is indeed being inserted.
On the UI side, I'm using @Query and a SwiftUI List to display the data but what I am seeing is that @Query is not updating as the data is being inserted. I have to quit and re-launch the app in order for the data to appear, almost like the context running the UI isn't communicating with the context in the ModelActor.
I've included a barebones sample project. To reproduce the issue, tap the 'Background Insert' button. You'll see logs that show items being inserted but the UI is not showing any data.
I've tested on the just released iOS 18b3 seed (22A5307f).
The sample project is here:
https://hanchor.s3.amazonaws.com/misc/SwiftDataBackgroundV2.zip
I see the issue a bug on SwiftData + SwiftUI side because @Query
is supposed to merge the change from the model actor (ModelActor
) and trigger a SwiftUI update, and so would suggest that you file a feedback report and post your report ID for folks to track.
For a workaround before the issue is fixed on the framework side, you might consider observing .NSManagedObjectContextDidSave
notification and triggering a SwiftUI update from the notification handler. For example:
import Combine
extension NotificationCenter {
var managedObjectContextDidSavePublisher: Publishers.ReceiveOn<NotificationCenter.Publisher, DispatchQueue> {
return publisher(for: .NSManagedObjectContextDidSave).receive(on: DispatchQueue.main)
}
}
struct MySwiftDataView: View {
@Query private var items: [Item]
// Use the notification time as a state to trigger a SwiftUI update.
// Use a state more appropriate to your app, if any.
@State private var contextDidSaveDate = Date()
var body: some View {
List {
ForEach(items) { item in
Text("\(item.timestamp)")
}
// Refresh the view by changing its `id`.
// Use a way more appropriate to your app, if any.
.id(contextDidSaveDate)
}
.onReceive(NotificationCenter.default.managedObjectContextDidSavePublisher{ notification in
contextDidSaveDate = .now
}
}
}
Best,
——
Ziqiao Chen
Worldwide Developer Relations.