SwiftData: Is syncing changes across ModelContext’s working in Beta 1?

If I make model changes in the mainContext using a batch operation like context.update OR on a background thread ModelContext ModelContext(container), my saved changes are not automatically reflected in the UI (a List of models).

Also ModelContext.willSave and ModelContext.didSave don't seem to get called.

  1. Will automatic UI updates work in an upcoming Beta but are NOT in Beta 1?
  2. Or will we refresh View/@Query based on a ModelContext.didSavenotification?
  3. Or should this be working in Beta 1 and I am missing how it works?

Replies

A context will save automatically periodically and on certain system events like didEnterBackground. If you want to save in the moment, you need to call context.save(). It might be possible that the autosave feature is buggy in Seed 1. The UI should update even if you don't manually save though.

I am saving after making these changes. In the case of a batch(?) update in the main modelContext...

let _ = try? context.update(expressions: ["isRead": NSExpression(forConstantValue: true)], model: Article.self, where: predicate)
try? context.save()

and in the case of a background thread with a new ModelContext...

let context = ModelContext(container)
context.autosaveEnabled = true
let article = Article(…)
context.insert(article)
try? context.save()

The changes are definitely getting saved to the DB. If I force refresh my View and its @Query, the new Articles appear.

In both cases, I also don't see didSave get called...

.onReceive(NotificationCenter.default.publisher(for: ModelContext.didSave)) { notification in
  print("😀 \(#function) didSave")
}
  • If you've got a concise sample project for this already built out, please submit it in a feedback and let us know if there's an answer!

Add a Comment

I experienced the same issue and filed FB12319007.

You can reproduce the issue by generating a SwiftData app in the Xcode 15 beta and making the following changes:

diff --git a/UpdateFromDifferentActorNotSyncing/ContentView.swift b/UpdateFromDifferentActorNotSyncing/ContentView.swift
index e508dc8..6c079d0 100644
--- a/UpdateFromDifferentActorNotSyncing/ContentView.swift
+++ b/UpdateFromDifferentActorNotSyncing/ContentView.swift
@@ -8,6 +8,23 @@
 import SwiftUI
 import SwiftData
 
+// Model actor to add items
+actor ItemAdder: ModelActor {
+	// Conformance to ModelActor
+	let executor: any ModelExecutor
+
+	init(container: ModelContainer) {
+		self.executor = DefaultModelExecutor(context: ModelContext(container))
+	}
+
+	// Calling this function does not update the items in ContentView
+	func addItem() {
+		let newItem = Item(timestamp: Date())
+		context.insert(newItem)
+		try! context.save()
+	}
+}
+
 struct ContentView: View {
     @Environment(\.modelContext) private var modelContext
     @Query private var items: [Item]
@@ -41,10 +58,13 @@ struct ContentView: View {
     }
 
     private func addItem() {
-        withAnimation {
-            let newItem = Item(timestamp: Date())
-            modelContext.insert(newItem)
-        }
+			// Get our container so we can pass it to the task
+			let container = modelContext.container
+
+			Task {
+				let adder = ItemAdder(container: container)
+				await adder.addItem()
+			}
     }
 
     private func deleteItems(offsets: IndexSet) {

This is the only solution that worked for me: observing .NSManagedObjectContextDidSave instead.

        notifCenter.publisher(for: .NSManagedObjectContextDidSave)
            .sink { [weak self] _ in
                self?.update()
            }
            .store(in: &cancellables)