iosswiftcore-datansmanagedobjectcontextnsmanagedobjectmodel

CoreData Object Move Between Entities Not Working


I have two entities and they have the exact same three attributes (name, desc, displayOrder), both have a handful of records, and my goal is to add/insert every item from "Entity 1" into "Entity 2".

Close, But No Cigar

I think my code is quite close. The console print out shows my code is successfully sending each item in "Entity 1" to "Entity 2" and saving it. BUT they save over each other! The first item is moved and saved, then the second item is moved but copies over the item that was previously moved. End result: only the last item moved actually shows up in the final "Entity 2".

Question: How do I fix this?

@IBAction func testOutMoveList(sender: AnyObject) {

    //Setup 'Do Later' context
    let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
    let managedContext = appDelegate.managedObjectContext!

    let new_Ent2_Item = NSEntityDescription.insertNewObjectForEntityForName("Entity2", inManagedObjectContext: managedContext)

    //Get sorted CoreData list and assign it to targetList_Cntxt
    let fetchRequest = NSFetchRequest(entityName: "Entity2")
    let sortDescriptor = NSSortDescriptor(key: "displayOrder", ascending: true)
    fetchRequest.sortDescriptors = [ sortDescriptor ]
    do {
        let fetchedResults = try managedContext.executeFetchRequest(fetchRequest) as? [NSManagedObject]

        if let results = fetchedResults {
            entity2_Cntxt = results
        }
    } catch {
        print(error)
    }

    for i in 0..<entity1.count {
        //Grab a Today task item
        let itemToMove = entity1_Cntxt[i]

        //Assign the Today item's contents to variables
        let nameToTransfer = itemToMove.valueForKey("name") as? String
        let descToTransfer = itemToMove.valueForKey("desc") as? String
        //Assign the Today item's contents to the target's 'task' object
        new_Ent2_Item.setValue(nameToTransfer, forKey: "name")
        new_Ent2_Item.setValue(descToTransfer, forKey: "desc")

        //Insert the item!!
        entity2_Cntxt.insert(new_Ent2_Item, atIndex: 0)
    }

//Updates target list in Core Data after append, delete, and drag/drop
func update_TargetDisplayOrder() {
    for i in 0..<entity2_Cntxt.count {
        let item = entity2_Cntxt[i]
        item.setValue( i, forKey: "displayOrder" )
    }
}

Possible Clue: I have noticed that the displayOrder doesn't seem to be updating correctly. The first item should be 0, the second should be 1, etc but instead each time the code cycles the lowest displayOrder is 1 higher (A three item list might start with values 2,3,4 - then my code moves/copies another item to entity 2 and the display order values are: 3,4,5)

Question: What code can I add or fix to make this transfer I'm attempting work!?!

Bonus question: Post-transfer how do I quickly/easily clear all the values from "Entity 1"


Solution

  • The short answer is that you need to move this line:

    let new_Ent2_Item = NSEntityDescription.insertNewObjectForEntityForName("Entity2", inManagedObjectContext: managedContext)
    

    so that it is inside the for loop:

    for i in 0..<entity1.count {
        //Grab a Today task item
        let itemToMove = entity1_Cntxt[i]
        // Create the corresponding new Entity2 object:
        let new_Ent2_Item = NSEntityDescription.insertNewObjectForEntityForName("Entity2", inManagedObjectContext: managedContext)
        //Assign the Today item's contents to variables
        let nameToTransfer = itemToMove.valueForKey("name") as? String
        let descToTransfer = itemToMove.valueForKey("desc") as? String
        //Assign the Today item's contents to the target's 'task' object
        new_Ent2_Item.setValue(nameToTransfer, forKey: "name")
        new_Ent2_Item.setValue(descToTransfer, forKey: "desc")
    
        //Insert the item!!
        entity2_Cntxt.insert(new_Ent2_Item, atIndex: 0)
    }
    

    To explain: the insertNewObjectForEntityForName is what actually creates the new Entity2 object. In its original position, that line is run only once, so only one Entity2 object is created. Your for loop then changes its attribute values and inserts it at the start of the entity2_Cntxt array (multiple times). Note that the last step in the for loop, entity2_Cntxt.insert(new_Ent2_Item, atIndex: 0) does not create a new object, or copy new_Ent2_Item, it just inserts it at the start of the array. With the amended code, a new Entity2 object will be created each time through the loop.

    Regarding the displayOrder problem, if you inspect the entity2_Cntxt array each time through the loop, you should find (with your original code) that the array contains the same Entity2 object several times, followed by the objects obtained from the fetch. Suppose entity2_Cntxt contains the new object three times (at indexes 0,1,2). Your update_TargetDisplayOrder method will then set that object's displayOrder to 0, then 1, then 2. And then the objects returned by the fetch will have displayOrder 3, 4, etc. I think this should all come good when you move the insertNewObjectForEntityForName as above.