Looking for tutorial on how to use SwiftData correctly in background threads

Hello community,

I am in search of a tutorial that comprehensively explains the proper utilization of SwiftData for updating model data in a background thread. From my understanding, there is extensive coverage on creating a model and loading model data into a view, likely due to Apple's detailed presentation on this aspect of SwiftData during WWDC23.

Nevertheless, I am encountering difficulties in finding a complete tutorial that addresses the correct usage of SwiftData for model updates in a background thread. While searching the web, I came across a few discussions on Stack Overflow and this forum that potentially provide an approach. However, they were either incomplete or proved ineffective in practical application.

I would greatly appreciate any links to tutorials that thoroughly cover this topic.

Answered by esperpanorama in 778784022

I figured out a working solution, tested on iOS 17.3 on device. I will copy/paste code snippets from around this forum to compile a short how to.

First, create a model actor, that is used to access swift data:

@ModelActor
actor MyModelActor {
    init(container: ModelContainer) {
        modelContainer = container
        let context = ModelContext(container)
        modelExecutor = DefaultSerialModelExecutor(modelContext: context)
    }
    
    // example usage
    func create() {
        let item = Item()
        modelContext.insert(item)
        try! modelContext.save()
    }
}

Snippet copied from https://developer.apple.com/forums/thread/736154 and modified for better readability.

Second, initialize the model actor in a detachted task:

Task.detached {
    let actor = MyModelActor()
   await actor.create()
}

Information from https://forums.developer.apple.com/forums/thread/736226

As of iOS 17.3 creating the model actor in a detached task is imperative because otherwise it will always execute on the main thread. This seems to be a bug in SwiftData that is covered extensively in the linked forum post. (this one was killing me)

Thanks to @Fat Xu and @rkhamilton for pointing me into the right directions.

Accepted Answer

I figured out a working solution, tested on iOS 17.3 on device. I will copy/paste code snippets from around this forum to compile a short how to.

First, create a model actor, that is used to access swift data:

@ModelActor
actor MyModelActor {
    init(container: ModelContainer) {
        modelContainer = container
        let context = ModelContext(container)
        modelExecutor = DefaultSerialModelExecutor(modelContext: context)
    }
    
    // example usage
    func create() {
        let item = Item()
        modelContext.insert(item)
        try! modelContext.save()
    }
}

Snippet copied from https://developer.apple.com/forums/thread/736154 and modified for better readability.

Second, initialize the model actor in a detachted task:

Task.detached {
    let actor = MyModelActor()
   await actor.create()
}

Information from https://forums.developer.apple.com/forums/thread/736226

As of iOS 17.3 creating the model actor in a detached task is imperative because otherwise it will always execute on the main thread. This seems to be a bug in SwiftData that is covered extensively in the linked forum post. (this one was killing me)

Thanks to @Fat Xu and @rkhamilton for pointing me into the right directions.

Looking for tutorial on how to use SwiftData correctly in background threads
 
 
Q