Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Data not persisted even after Core Data context save

I am facing some issues with the persistence of my core datas. I am using a NSSQLiteStoreType persistentStore (see below). In my application, everytime I am modifying, creating or deleting some data I am instantly saving these changes in my coredata by calling the function above saveContext()

BUT, time to time these changes are not persisted ! (this problem of non persistence is not always occuring, I still didnt succeed to reproduce this bug on purpose)

Here is the lifeCycle of one of these data:

  • Modification by the user
  • save context (patient.managedObjectContext where patient is the main entity linked to all others data entities)
  • synchronization with the backend using AlamoFire
  • set the remote id in the data after receiving the backend answer
  • save once again the context (still in Alamofire completion handler: executed in the main thread, checked by printing Thread.current)

Finally, when this bug happens, it does not affect only one data but seems to affect all data modified and saved from the current session (from the last opening until the closing)

Here is the code of my coredatacontroller:

class CoreDataController: NSObject {

static let shared = CoreDataController()

var managedObjectContext: NSManagedObjectContext


override init() {
    // This resource is the same name as your xcdatamodeld contained in your project.
    guard let modelURL = Bundle.main.url(forResource: "xxx", withExtension:"momd") else {
        fatalError("Error loading model from bundle")
    }
    // The managed object model for the application. It is a fatal error for the application not to be able to find and load its model.



    guard let mom = NSManagedObjectModel(contentsOf: modelURL) else {
        fatalError("Error initializing mom from: \(modelURL)")
    }
    let psc = NSPersistentStoreCoordinator(managedObjectModel: mom)

    let options = [ NSInferMappingModelAutomaticallyOption : true,
                    NSMigratePersistentStoresAutomaticallyOption : true]


    managedObjectContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
    managedObjectContext.persistentStoreCoordinator = psc

        let urls = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
        let docURL = urls[urls.endIndex-1]
        /* The directory the application uses to store the Core Data store file.
         This code uses a file named "DataModel.sqlite" in the application's documents directory.
         */
        let storeURL = docURL.appendingPathComponent("xxx.sqlite")
        do {
           try psc.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: storeURL, options: options)
        } catch let error  {
            fatalError("Error migrating store: \(error)")
        }
}

}

Here is where my coredatacontroller is instantiate:

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

var window: UIWindow? = UIWindow(frame: UIScreen.main.bounds)
let coreDataController : CoreDataController = CoreDataController.shared

// FIXME: determiner le comportement attendu pour le badge sur l'icone et l'appliquer !!
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    guard let window = window else {
        fatalError("Pas de window !!!")
    } ......

And here is my function used everytime i want to save a data in my coreData:

extension NSManagedObjectContext {

func saveContext() {
    do {
        try self.save()
    } catch let error  {
       print("Failure to save context: \(error)")
    }
}

}

Hope this problem will inspire you, and thank you by advance for your ideas !

EDIT 1:

After further investigation, all my process of synchronization (including alamofire requests) are done in the main thread (I checked on every step by printing Thread.current).

 {number = 1, name = main}
thread of dataFromJson
{number = 1, name = main}
thread of conficthandler
{number = 1, name = main}
thread of fetchDataToSync
{number = 1, name = main}

Moreover, after checking differences between my backend database and my coredata, it seems that coredata fails to save every data until the crash of the app (voluntary or not) and then the relaunch of the app which will 'reboot' my coredata controller.

like image 283
PaulM Avatar asked Dec 05 '25 14:12

PaulM


1 Answers

It's hard to be certain but there's one combination of details that spells trouble and might be the cause of this problem. You mention

save once again the context (still in the thread of Alamofire)

Looking through the code I see this:

managedObjectContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)

And later on there's this:

do {
    try self.save()
} catch let error  {
   print("Failure to save context: \(error)")
}

So, you have a context that uses main-queue concurrency, but you're saving changes off of the main thread by just calling save(). That's not good. Core Data is not thread-safe, and you're crossing threads.

To fix this, you really need to use the perform or performAndWait method on your managed context whenever you use it off of the main thread. That includes all access, everything that might touch the context in any way-- fetch requests, accessing properties or relations of managed objects, and saving changes. That's how Core Data wants to handle threading.

like image 81
Tom Harrington Avatar answered Dec 07 '25 04:12

Tom Harrington



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!