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() {
            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?