Search code examples
iosswiftcore-datansmanagedobjectcontext

Core Data: Update on background NSManagedObjectContext not working upon save


I have two managed object contexts and I want to use each one for fetch vs update as commonly advised.

However, all my attempts to utilize newBackgroundContext() to save my data have failed. performBackgroundTask also hasn't worked.

I am successful in saving using viewcontext (the code below is successful), but it defeats the purpose of having background save capability. Plus, it could result in bad performance in future.

I already tried setting automaticallyMergesChangesFromParent = true on my both contexts, but to no avail.

CoreDataManager in my below code is a boilerplate class setting up the lazy vars returning persistentContainer and context objects.

Is there a proper way to indicated anywhere how to handle this standard problem?

PS: When I try inserting a bulk of objects using my newBackgroundContext(), it just works OK. Update is the only problem - probably because it involves fetch which is handled by viewContext.

PS+: For that matter, if I use newBackgroundContext() for fetch and save both (just in the below function, not in my UI), it works too. But I do not know if this is recommended approach.

PS++: I also tried wrapping newBackgroundContext() under a lazy var, so as not to create a new one every time, which anyway happens inside performBackgroundTask - but this approach still does not save successfully.

class func updateRecord(recordId: String, data: Data)
{
    let fetchRequest = NSFetchRequest<MyModel>(entityName: "MyModel")
    let context = CoreDataManager.sharedInstance.queryContext  //wrapper for persistentContainer.viewContext which is used to fetch the intended object to update

    do
    {
        fetchRequest.predicate = NSPredicate(format: "recordId == %@", recordId)
        let result = try context.fetch(fetchRequest)
        guard let myModel = result.first
        else
        {
            throw CoreDataCustomError.ObjectNotFound
        }

        myModel.data = data

        try context.save() //works FINE!

        //what DOES NOT work:  try persistentContainer.newBackgroundContext().save() 

        //what DOES NOT work:  
        //persistentContainer.performBackgroundTask { (context) in
        //    context.save()
        //}
    }
    catch let customErr as CoreDataCustomError
    {
        print("Object does not exist in DB: \(customErr)")
    }
    catch let err
    {
        print("Error saving data \(recordId): \(err)")
    }
}

Solution

  • If I use newBackgroundContext() for fetch and save both (just in the below function, not in my UI), it works too. But I do not know if this is recommended approach

    Yes, it is the correct approach.

    When you initialise data in some context, you have to save it within the same context. If you try to save another context it will not work because this other context have no changes.

    When you fetch some data using one context:

    let result = try context.fetch(fetchRequest)
    

    and then you modify it:

    myModel.data = data
    

    it is visible only in this context. Which means if you try to save another context it will not see the change to the model and your data will not be saved.