iosswiftcore-datacore-data-migration

How to fetch relationship entities when performing a core data migration?


I am performing a core data migration. I have this subclass for my migration:

class TagtoSkillMigrationPolicyV1toV2: NSEntityMigrationPolicy {

    override func createDestinationInstances( forSource sInstance: NSManagedObject, in mapping: NSEntityMapping, manager: NSMigrationManager) throws {

        // 1 - create destination instance of skill and skillmetadata
        let skillEntityDescription = NSEntityDescription.entity( forEntityName: "Skill",in: manager.destinationContext)
        let newSkill = Skill(entity: skillEntityDescription!, insertInto: manager.destinationContext)

        let skillMetaDataDescription = NSEntityDescription.entity(forEntityName: "SkillMetaData", in: manager.destinationContext)
        newSkill.metaData = SkillMetaData(entity: skillMetaDataDescription!, insertInto: manager.destinationContext)

        var posts = sInstance.value(forKey: "posts")
        posts.sort {
            ($0.timeStamp! as Date) < ($1.timeStamp! as Date)
        }
        if let mostRecentThumbnail = posts.first?.postImage {
            let thumbnail = Utils.makeThusmbnail(url: URL(string: mostRecentThumbnail)!, for: 150.0)
            newSkill.metaData?.thumbnail = thumbnail?.pngData()
        }

        if let name = sInstance.value(forKey: "name"){
            newSkill.setValue(name, forKey: "name")
        }
        manager.associate(sourceInstance: sInstance, withDestinationInstance: newSkill, for: mapping)
    }
}

This custom migration is from my old Tag entity to my new Skill entity. For each Tag, it creates a skill entity and a skillmetaData entity that it attaches to it. There are no entries in the .xcmappingmodel file, as I am doing everything manually - please let me know if I still need entries there.

Right now, when it runs, it gives a signal SIGABRT:

Could not cast value of type '_NSFaultingMutableOrderedSet' (0x7fff87c44e80) to 'NSArray' (0x7fff87c51fb0).
2020-04-14 15:55:40.108927-0700 SweatNetOffline[28046:3645007] Could not cast value of type '_NSFaultingMutableOrderedSet' (0x7fff87c44e80) to 'NSArray' (0x7fff87c51fb0).

When I set a breakpoint and inspect sInstance.value(forKey: "posts"), I get

▿ Optional<Any>
  - some : Relationship 'posts' fault on managed object (0x6000010dcf00) <NSManagedObject: 0x6000010dcf00> (entity: Tag; id: 0xdf4b05c2e82b2c4e <x-coredata://A6586C18-60C3-40E7-B469-293A50EB5728/Tag/p1>; data: {
    latestUpdate = "2011-03-13 00:17:25 +0000";
    name = flowers;
    posts = "<relationship fault: 0x600002607f40 'posts'>";
    uuid = "4509C1B3-7D0F-4BBD-AA0B-7C7BC848DA80";
})

so its not entirely loading those posts - its giving that fault there.

The goal is to get the thumbnail from the latest of those posts. How can I access this relationship item?


Solution

  • I found a way to do it using

    let lastThumbnail = sInstance.mutableOrderedSetValue(forKeyPath: "posts.postImage").lastObject
    

    Reading this helped a lot. My understanding is at this point in the migration, we are dealing with NSManagedObjects and never the actual Model which would have attributes like "posts" that it usually does in core data.

    In Objective-C especially and swift to some extent, NSObjects are accessed with key-value coding. They are accessed indirectly - meaning instead of reading the object itself, you are reading what the NSMutableOrderedSet wants to show you for that key - so it can do stuff to it before presenting it. Thus you can't for instance, fetch just "forKey: posts" and then expect posts to have the attributes your post usually does. Anyone is welcome to flesh out this explanation if they know more :).

    One thing I was trying to do is to fetch by that key-path in a sortable way - so that I can find the post that has the most recent timestamp and then use that associated thumbnail. Instead I am using .lastObject which works because this is an NSMutableOrderedSet and not an NSMutableSet because my original model specified this relationship to be an ordered relationship. This means that it will pick the last inserted item which is ok for me for now. Still interested in a way to filter that result though if anyone has advice.