Search code examples
swiftcore-dataconcurrencynsmanagedobjectcontext

Using CoreData managedContext from background thread...how do you do it correctly?


I have the CoreData concurrency debugger on and I am asserting every where. I cannot figure it out.

I created what I thought was a context on a background thread. Please take a look at my CoreData Stack:

import Foundation
import CoreData

class CoreDataManager {

  lazy var managedContext: NSManagedObjectContext = {
    return self.storeContainer.viewContext
  }()
  
  lazy var backgroundContext: NSManagedObjectContext = {
    
    return self.storeContainer.newBackgroundContext()
  }()
  
  lazy var privateMOC: NSManagedObjectContext = {
    let pMOC = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
    pMOC.parent = self.storeContainer.viewContext
    return pMOC
  }()
  
  private lazy var storeContainer: NSPersistentContainer = {
    let container = NSPersistentContainer(name: "XXXX")
    
    
    container.loadPersistentStores(completionHandler: { (storeDescription, error) in
      if let error = error as NSError? {
        print("Unresolved error \(error), \(error.userInfo)")
      }
    })
    return container
  }()
    
  func saveContext () {
    guard managedContext.hasChanges else { return }
    
    do {
      try managedContext.save()
    } catch let error as NSError {
        print("Unresolved error \(error), \(error.userInfo)")
    }
    
    self.reset()
  }
  
  func reset() {
    managedContext.reset()
  }
  
}

Then I try to perform a task from a background thread from within a repository type class:

  func deleteAllData() {
    let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "Pairing")
    let pairings = (try? self.coreDataManager.privateMOC.fetch(fetchRequest)) as! [Pairing_ManagedObject]. // I GET THE ASSERT HERE
    for pairing in pairings {
      self.coreDataManager.privateMOC.delete(pairing)
    }
    self.coreDataManager.saveContext()
  }

How do I do this so I don't get he core data concurrency assert and do it correctly? Please help.


Solution

  • You're creating a new managed object context with private queue concurrency, but that's not enough to avoid concurrency issues. Core Data's policy is that you must use either performBlock or performBlockAndWait for everything you do that uses that context. In your code that means that the delete and save calls must be be done with one of those functions.

    There's a second issue though. You're using your saveContext function to save changes. But that doesn't save changes on your private-queue context. It saves on some other context called managedContext. Saving on one context doesn't automatically save other contexts. If you want to make changes on your private queue context and save those changes, you need to save that context at some point.