NEW: Start my new Ultimate Portfolio App course with a free Hacking with Swift+ trial! >>

How do I run background tasks on CoreData Entities?

Forums > SwiftUI

I have an app that generates top level CoreData Entities that have multiple levels of child entities that take some time to generate. I'm trying to avoid locking up the main thread so that the app doesn't become unresponsive.

I've attempted to create a second background ManagedObjectContext to run on a different thread, first by creating a new EnvironmentKey:

private struct BackgroundMOCEnvironmentKey: EnvironmentKey {
    static let defaultValue: NSManagedObjectContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)

extension EnvironmentValues {
    var backgroundManagedObjectContext: NSManagedObjectContext {
        get { self[BackgroundMOCEnvironmentKey.self] }
        set { self[BackgroundMOCEnvironmentKey.self] = newValue }

I've then passed this to the ContentView() in the App swift file containing a new background Managed Object Context:

var body: some Scene {
        WindowGroup {
                .environment(\.managedObjectContext, persistenceController.container.viewContext)
                .environment(\.backgroundManagedObjectContext, persistenceController.container.newBackgroundContext())
        .onChange(of: scenePhase) { _ in

In the struct I've written to perform the background tasks, I reference the new background Managed Object Context

struct GameDataGenerator {
    @Environment(\.backgroundManagedObjectContext) var moc

    func generateData(forObjectID objectID: NSManagedObjectID, gridSize: Int, gameDepth: Int) {
        let managedObject = moc.object(with: objectID)      // The parent Entity

In the ContentView onAppear modifier, I run a process that genererates, and saves, the top level Entity on the main thread using environment(.managedObjectContext) so that it appears in the UI.

I then call generateData() asyncronously, passing it the objectID so that it can carry on generating the rest of the data:

let generator = GameDataGenerator()
        DispatchQueue.main.async {
            generator.generateData(forObjectID: gameTile.objectID, gridSize: gridSize, gameDepth: gameDepth)

I'm getting an error saying that the "persistent store is not reachable from this NSManagedObjectContext's coordinator".

Please could anyone advise if there is a better/simpler way to run background tasks against CoreData Entities, or see if they can spot a problem with my approach above?



You can run background tasks via the NSPersistentContainer, which will give you new context to work with. The changes are then automatically merged to the view context if configured:

context.automaticallyMergesChangesFromParent = true

And the method to run the backgrond task is this one: performBackgroundTask.

For more details you can check out my blog post about setting up Core Data stack with background work support.



Thank you so much.

I have been playing around with performBackgroundTask just now after finding the apple documentation on it.

I'm setting a new environment key so that I can reference the container in order to call performBackgroundTask:

.environment(\.persistenceContainer, persistenceController.container)

The part I was missing to get this working was:

context.automaticallyMergesChangesFromParent = true

I'm now setting this in the persistence.swift file.

Your blog post looks good though so I'm going to look at using that for the merge changes notifications.


Hacking with Swift is sponsored by Instabug

SPONSORED Catch bugs as soon as they happen and know exactly why a crash occurred. Instabug's SDK grabs all the logs they need to fix bugs, crashes and performance issues in minutes instead of days. Get screenshots, device details, network logs, repro steps, and tons of other critical insights needed to resolve issues and prioritize product backlogs straight from your dashboard. It only takes a minute to integrate!

Get started now

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

Unknown user

You are not logged in

Log in or create account

Link copied to your pasteboard.