SwiftData ignore changes on App Intents

Hello everyone, Xcode 16.0 SwiftData project. CloudKit. WidgetConfigurationIntent.

For some reason, I see a really weird behavior.

I have a shared ModelContainer and an interactive widget where I update the model data through an app intent.

This is my model -

@MainActor
class ItemsContainer {
    static let shared = ItemsContainer()
    var sharedModelContainer: ModelContainer!
    
    init() {
        self.sharedModelContainer = container()
    }
    
    func container() -> ModelContainer? {
        if let sharedModelContainer {
            return sharedModelContainer
        }

        let schema = Schema([
            Session.self,
        ])
        let modelConfiguration: ModelConfiguration
        modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false, cloudKitDatabase: .automatic)

        do {
            let container = try ModelContainer(for: schema, configurations: [modelConfiguration])
            self.sharedModelContainer = container
            return container
        } catch {
            fatalError("Could not create ModelContainer: \(error)")
        }
    }
}

And this is how I get the model context across the app and the app intent -

        let modelContext = ModelContext(ItemsContainer.shared.sharedModelContainer)

The problem is that somehow, when I update the model context in the app and then in the widget (I save the context after every change), the data is synced between the app and the widget, but then, the data is changed back to the previous state and kind of ignores the widget changes.

Didn't happen before iOS 18/Xcode 16.

Any idea?

Thanks a lot!

Answered by DTS Engineer in 805463022

The SwiftData version of the Adopting SwiftData for a Core Data app does pretty much the same thing as what you described, except that it doesn't use CloudKit, and so you can probably start with checking if the sample demonstrates the same issue on your side.

To figure out what really happens when the value changes, you can use the SwiftData history API to dump the history and analyze the update transaction (HistoryUpdate). The sample mentioned above demonstrates how to use the history API as well.

If you are using SwiftData + CloudKit, which is based on NSPersistentCloudKitContainer today, in an extension, you might want to look at the following technote section:

Best,
——
Ziqiao Chen
 Worldwide Developer Relations.

The SwiftData version of the Adopting SwiftData for a Core Data app does pretty much the same thing as what you described, except that it doesn't use CloudKit, and so you can probably start with checking if the sample demonstrates the same issue on your side.

To figure out what really happens when the value changes, you can use the SwiftData history API to dump the history and analyze the update transaction (HistoryUpdate). The sample mentioned above demonstrates how to use the history API as well.

If you are using SwiftData + CloudKit, which is based on NSPersistentCloudKitContainer today, in an extension, you might want to look at the following technote section:

Best,
——
Ziqiao Chen
 Worldwide Developer Relations.

Accepted Answer

"I open the app. Few seconds later I receive two new tokens (183 and 184) which are the mirroring of 180 & 181. They override 182. New state = 181 instead of 182. This is the bug."

This does sound like a bug to me. I unfortunately don't see any workaround because the synchronization logic is all encapsulated in NSPersistentCloudKitContainer. I’d suggest that you file a feedback report for the Core Data + CloudKit folks to take a look – If you do so, please share your report ID here for folks to track.

As discussed in Avoid synchronizing a store with multiple persistent containers though, I'd probably have the main app, and not its extension, do the CloudKit synchronization.

Best,
——
Ziqiao Chen
 Worldwide Developer Relations.

SwiftData ignore changes on App Intents
 
 
Q