A few observations on the behaviour in iOS 18.0(.0 and .1) as well as the latest 18.1 beta:
changes from background contexts seem to sometimes be merged into the main context at irregular intervals
disabling autosave on the main context seems to also prevent propagation of any changes from background contexts
listening to .NSManagedObjectContextDidSave as suggested by DTS Engineer only helps in the most basic cases
The first two points make it feel like the MainActor context my SwiftUI views use only checks and merges changes whenever the internal autosave is triggered, even if there have been multiple notifications that the underlying NSManagedObjectContext has been saved by other sources.
As an experiment I tried to manually trigger modelContext.save() for the main context whenever I caught a notification for a background save however this doesn't seem to help with merging of background changes (it actually has no effect - the new data is in the store but it never makes it to any @Query update).
With no mentions of SwiftData in any of the Release Notes (18.0.x as well as 18.1 beta) it's anything but easy to keep faith in SwiftData in it's current, very fragile feeling implementation.
One point especially is giving me grey hairs right now - if an @Model contains an array of other @Model's and a property of one of those changes via a background context the changes do not propagate at all even if the workaround by DTS Engineer is applied.
import SwiftData
@Model
final class HistoryRecord {
// history records are added regularly via a background @ModelActor that also calls .save() on it's modelContext
var recordedAt: Date
var logbook: Logbook?
}
@Model
final class Logbook {
// every time a HistoryRecord is added lastUpdate is also set to the current date
var lastUpdate: Date
@Relationship(delete: .cascade)
var records: [HistoryRecord] = []()
var shelf: Bookshelf?
}
@Model
final class Bookshelf {
@Relationship(delete: .cascade)
var books: [Logbook]
}
struct BookshelfView: View {
@Environment(\.modelContext) private var modelContext
@Query shelves: [Bookshelf]
var body: some View {
ForEach(shelves) { shelf in
ForEach(shelf.books) { book in
// this number doesn't go up at all
Text("\(book.records.count) records in book")
}
}
}
}