Core Data Migration: Could not cast value of type

2019-07-31 10:44发布

I'm doing a 'medium-weight' Core Data Migration. I'm using a Mapping Model to migrate from one legacy store / data model to a different store and different model (i.e. completely different .xcdatamodeld) files, and using custom NSEntityMigrationPolicy objects where applicable.

Where previously I had all sorts of objects unrelated on the object graph, I now want to have a master object Library that would enable me to easily wipe out all of the associated data (using the Cascade delete rule).

I've run into problems during the migration because of a custom method in my NSEntityMigrationPolicy subclass:

class LegacyToModernPolicy: NSEntityMigrationPolicy {

func libraryForManager(_ manager: NSMigrationManager) -> Library {

    let fetchRequest: NSFetchRequest<Library> = NSFetchRequest(entityName: Library.entity().name!)

    fetchRequest.predicate = nil
    fetchRequest.sortDescriptors = [NSSortDescriptor(key: "filename", ascending: true)]
    fetchRequest.fetchLimit = 1

    do {
        // will fail here if NSFetchRequest<Library>
        let results = try manager.destinationContext.fetch(fetchRequest)  
        log.info("results: \(results)")

        if results.count == 1 {
            // can fail here if NSFetchRequest<NSManagedObject>
            return results.first! as! Library  
        } else {
            let newLib = Library(context: manager.destinationContext)
            return newLib
        }

    } catch {
        log.error("Error fetching: \(error.localizedDescription)")
    }

    let newLib = Library(context: manager.destinationContext)
    return newLib
  }
}

An exception will be thrown, and the error message is:

Could not cast value of type 'NSManagedObject_Library_' (0x6100000504d0) to 'SongbookSimple.Library' (0x101679180).

The question is, why is that happening, and does it matter? Because a migration is happening, perhaps it's enough to return the NSManagedObject with the correct entity description ?

1条回答
狗以群分
2楼-- · 2019-07-31 10:54

The reason is that during migration, you should not be using instances of NSManagedObject subclasses. You need to express all of these in the form of NSManagedObject. So the code above must become:

class LegacyToModernPolicy: NSEntityMigrationPolicy {

static func find(entityName: String,
                 in context: NSManagedObjectContext,
                 sortDescriptors: [NSSortDescriptor],
                 with predicate: NSPredicate? = nil,
                 limit: Int? = nil) throws -> [NSManagedObject] {

    let fetchRequest: NSFetchRequest<NSManagedObject> = NSFetchRequest(entityName: entityName)
    fetchRequest.predicate = predicate
    fetchRequest.sortDescriptors = sortDescriptors
    if let limit = limit {
        fetchRequest.fetchLimit = limit
    }

    do {
        let results = try context.fetch(fetchRequest)
        return results
    } catch {
        log.error("Error fetching: \(error.localizedDescription)")
        throw error
    }
}

func libraryForManager(_ manager: NSMigrationManager) -> NSManagedObject {

    do {
        var library: NSManagedObject? = try LegacyToModernPolicy.find(entityName: Library.entity().name!,
                                                in: manager.destinationContext,
                                                sortDescriptors: [NSSortDescriptor(key: "filename", ascending: true)],
                                                with: nil,
                                                limit: 1).first
        if library == nil {
            let dInstance = NSEntityDescription.insertNewObject(forEntityName: Library.entity().name!, into: manager.destinationContext)

            // awakeFromInsert is not called, so I have to do the things I did there, here:
            dInstance.setValue(Library.libraryFilename, forKey: #keyPath(Library.filename))
            dInstance.setValue(NSDate(timeIntervalSince1970: 0), forKey: #keyPath(Library.updatedAt))
            library = dInstance
        }

        return library!

    } catch {
        fatalError("Not sure why this is failing!")
    }
}}

You can read more about my less-than-fun experiences with Core Data Migrations here.

查看更多
登录 后发表回答