Live queries on SwiftData DB but without @Query macro?

I switched from using @Query to @ModelActor because of the following reasons:

Performance Issues: With @Query, my app became unresponsive with large datasets because data fetching occurred on the main thread.

Integration with CKSyncEngine: I needed to implement @ModelActor anyway to allow CKSyncEngine to add data to local persistent storage from the background.

In my current setup, the onAppear() method for my view calls the getItems() function on my model actor, which returns [ItemsDTO] and then View renders them.

However, I'm now facing a challenge in achieving the same automatic data refreshing and view updates that @Query provided. Here are a few potential solutions I'm considering:

Periodic Data Fetching: Fetch data at regular intervals to keep the view updated. But this seems expensive.

Local Write Monitoring: Monitor all local writes to automatically trigger updates when changes occur. But this is quite a lot of code I would have to write myself.

Switch to manual refresh: Users would have to manually trigger UI updates by pressing button.

Reintroduce @Query: Writes would happen from ModelActor, but reads would still happen from Main thread. But then again app would become unresponsive on reads.

If you have any additional ideas or best practices for maintaining reactivity with @ModelActor, I'd love to hear them!

This is indeed a case where @Query doesn't quite fit, because @Query is tied to a main queue context, and if you make a lot of changes on your store, @Query will need some time to merge the changes, which may block the main queue.

Ideally, I'd consider if I can re-design my UI so it presents a smaller data set. @Query should work well if the relevant data set isn't that large.

If that is not a choice, I'd consider the following:

  1. Create a model actor (ModelActor) and let it do both data saving and fetching. The actor works in the background, and so won't block the UI.

  2. When the model actor has done the data saving, it triggers a SwiftUI update using Observation or notifications.

  3. The relevant SwiftUI views request data from the modal actor.

  4. The modal actor fetches the relevant SwiftData models, transforms them to a Sendable type, and passes the data the views. The Sendable type can be an array of a Swift struct that contains the data needed to refresh the UI.

Step 4 introduces a new data type because a SwiftData model isn't Sendable and can't be passed across actors directly. I can pass a set of SwiftData model identifiers across actors, but then I need to convert the identifiers to objects using a main queue model context, which may again block the UI.

Best,
——
Ziqiao Chen
 Worldwide Developer Relations.

Live queries on SwiftData DB but without @Query macro?
 
 
Q