0 Replies
      Latest reply on Feb 12, 2020 4:42 PM by tschmitz
      tschmitz Level 1 Level 1 (0 points)

        (I also posted this on StackOverflow at https://stackoverflow.com/questions/60196113/core-data-migratepersistentstore-crash-with-perform-performandwait)

         

        I'm attempting to use `NSPersistentStoreCoordinator.migratePersistentStore(_:to:options:withType:)` to move a persistent store from one location to another, using an approach like this: https://useyourloaf.com/blog/moving-core-data-files/. (My overall goal is to create a backup, so I'm also drawing on the Ole Begemann's work (https://oleb.net/blog/2018/03/core-data-sqlite-backup/), which is in turn inspired by a previous SO post (https://stackoverflow.com/questions/22670273/copy-backup-persistent-store/22672386#22672386), but the problem I encounter is the same in both cases.)

         

        let sourceStore = ... // NSPersistentStore
        let backupFileURL = ... // URL
        try self.migratePersistentStore(sourceStore, to: backupFileURL, options: [:], withType: NSSQLiteStoreType)

         

        When calling `migratePersistentStore`, Core Data loads objects into memory, triggering a call to `awakeFromFetch()` on each one. That uses some memory, no surprise, but not necessarily a big deal in this case.

         

        However, if `awakeFromFetch()` happens to do something that uses `NSManagedObjectContext.perform` or `NSManagedObjectContext.performAndWait`, the app crashes while moving the persistent store with an error like this one:

         

        *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Can only use -performBlock: on an NSManagedObjectContext that was created with a queue.'

         

        As far as I can tell, the crash occurs because during the move/migration, `awakeFromFetch()` is being called in an NSManagedObjectContext that uses the deprecated `confinementConcurrencyType` and calls to perform/performAndWait aren't permitted on with that concurrency type.

         

        (Here's a super simple example `awakeFromFetch()` that I'm using as a demonstration:)

        public override func awakeFromFetch() {
            super.awakeFromFetch()
            managedObjectContext?.perform {
          print("Hello, world!")
          }
        }

         

         

        I could theoretically work around this by checking the context's concurrency type and only wrap calls in perform/performAndWait if it's not a confinement context, but that seems both unwieldy and out of line with best practices.

         

         

        Does anyone have a recommendation for how to handle this? Is there a way to prompt `migratePersistentStore` to use a different concurrency type?