WWDC21 SALE: Save 50% on all my Swift books and bundles! >>

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 {
            ContentView()
                .environment(\.managedObjectContext, persistenceController.container.viewContext)
                .environment(\.backgroundManagedObjectContext, persistenceController.container.newBackgroundContext())
        }
        .onChange(of: scenePhase) { _ in
            persistenceController.save()
        }
    }

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?

Thanks!

   

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.

   

Filip,

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.

1      

Save money with our WWDC sale!

SAVE 50% To celebrate WWDC21, all our books and bundles are half price, so you can take your Swift knowledge further without spending big! Get the Swift Power Pack to build your iOS career faster, get the Swift Platform Pack to builds apps for macOS, watchOS, and beyond, or get the Swift Plus Pack to learn advanced design patterns, testing skills, and more.

Save 50% on all our books and bundles!

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.