Using SwiftData in background?

How does SwiftData work with background operations? CoreData had background context that could be used to avoid UI hang for heavy operations.

Is there an equivalent in SwiftData, and if so, do I have to merge changes or does it save directly to persistent store?

Post not yet marked as solved Up vote post of jenyalebid Down vote post of jenyalebid
3k views

Replies

From the WWDC lounge:

WWDC Bot APP 8 days ago @Duncan asked: As I understand it, SwiftData model objects are not thread-safe, just like NSManagedObjects. Are there any additional mechanisms to make managing this easier for us than it was in traditional Core Data? e.g., compiler warnings before passing an object out of its context? 2 replies

WWDC Bot APP 8 days ago @Dave N (Apple) answered: We have provided the ModelActor protocol & the DefaultModelExecutor to make SwiftData work with Swift Concurrency. Use your ModelContainer to initialize a ModelContext in the initializer for your ModelActor conforming actor object and use that context to initialize a DefaultModelExecutor. This will allow you to use that context with async functions on your actor.

So something like this:

actor BackgroundPersistence: ModelActor {
    
    nonisolated public let executor: any ModelExecutor
            
    init(container: ModelContainer) {
        let context = ModelContext(container)
        executor = DefaultModelExecutor(context: context)
    }

    func store(data: MyModel) throws {
        context.insert(data)
        try context.save()
    }

    func loadAllData() throws -> [MyModel] {
        let predicate = #Predicate<MyModel> { data in 1==1 }
        let sort = SortDescriptor<MyModel>(\.date)
        let descriptor = FetchDescriptor(predicate: predicate, sortBy: [sort])
        
        let result = try context.fetch(descriptor)

        return result
    }
}

And then use it like this:

@MainActor struct RouteView: View {

    @Environment(\.modelContext) private var context: ModelContext

    var body: some View {
        Button("Save") {
            save()
        }
    }

    private func save() {
        Task(priority: .userInitiated) {
            do {
                let persistence = BackgroundPersistence(container: context.container)
                try await persistence.store(data: myData)    
            
                // Not sure if saving the main context is also needed
                try context.save()
            } catch {
                print(error.localizedDescription)
            }
        }
    }

Inserting and fetching works for me like this. My only problem is that after terminating the app, all the data is gone. I guess there needs to be some kind of way to tell SwiftData to merge the background context into the main context.

  • Facing a similar issue too with contexts residing in interactive widgets. Except the data isn’t lost, it just requires a restart of the main app to get the updated data. Data seems to merge just fine even when editing the outdated data in the main app, but it’s still an issue.

Add a Comment

Did you ever find a solution? im having the same issue, where after the app is restarted, all the data is lost, but only once I start accessing the swift data from the background context.

Same problem!!

anyone solve this??

thanks.

Any update on this one???