UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

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!

3      

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.

3      

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.

4      

Hacking with Swift is sponsored by Essential Developer

SPONSORED Join a FREE crash course for mid/senior iOS devs who want to achieve an expert level of technical and practical skills – it’s the fast track to being a complete senior developer! Hurry up because it'll be available only until April 28th.

Click to save your free spot now

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

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

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.