Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS9 Xcode 7 - Core Data - avoiding duplicated objects

As described in WWDC2015 presentation video, in the new Xcode7 we can set the objects uniqueness directly in the Xcode model editor. I was trying to implement this my code, but something is not working as expected. When I try to save duplicated object, the Xcode rejects the save, but the table updates with duplicated cell.

So I have set the unique attributes startdate and enddate.

enter image description here

Then I have modified my save function to handle the error and inform the user by UIAlertController.

func addContract() {
    do {
        let appDelegate: AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
        let context: NSManagedObjectContext = appDelegate.managedObjectContext

        let entity = NSEntityDescription.entityForName("Contract", inManagedObjectContext: context)
        let newContractData = Contract(entity: entity!, insertIntoManagedObjectContext: context)

        newContractData.startdate = dateFormatter.dateFromString(startDateTextField.text!)!
        newContractData.enddate = dateFormatter.dateFromString(endDateTextField.text!)!
        newContractData.ship = shipNameTextField.text!
        newContractData.position = positionOnBoardTextField.text!
        newContractData.workingdays = Int(workingDaysLabel.text!)!

        try context.save()
    } catch {
        let alertController = UIAlertController(
            title: "Error",
            message: "The contract exsist",
            preferredStyle: UIAlertControllerStyle.Alert)
        let okAction = UIAlertAction(
            title: "OK",
            style: UIAlertActionStyle.Cancel,
            handler: nil)
        alertController.addAction(okAction)
        presentViewController(alertController, animated: true, completion: nil)
    }
}

So far so good, but when I go back to the root controller with the cancel button, the table appears updated with duplicate cell.

@IBAction func cancelButtonPressed(sender: UIBarButtonItem) {
    self.navigationController?.popToRootViewControllerAnimated(true)
}

In addition, stop and run the application removes the duplicates.

Here it is a video of the problematic behaviour.

The generated error is as follow:

Error Domain=NSCocoaErrorDomain Code=1551 "The operation couldn’t be completed. (Cocoa error 1551.)" UserInfo=0x7fc02d462190 {Conflicts=(
        {
        constraint =         (
            startdate,
            enddate
        );
        entity = Contract;
        objects =         (
            "<Contract: 0x7fc02d45ba60> (entity: Contract; id: 0x7fc02d019430 <x-coredata:///Contract/t0897571B-200B-4F04-AF87-D50831E2DE672> ; data: {\n    enddate = \"2017-06-13 21:00:00 +0000\";\n    position = test;\n    ship = test;\n    startdate = \"2016-06-13 21:00:00 +0000\";\n    workingdays = 366;\n})",
            "<Contract: 0x7fc02b7433c0> (entity: Contract; id: 0xd000000000100000 <x-coredata://C3318932-BEDB-4AB6-A856-103F542BCF44/Contract/p4> ; data: {\n    enddate = \"2017-06-13 21:00:00 +0000\";\n    position = test;\n    ship = test;\n    startdate = \"2016-06-13 21:00:00 +0000\";\n    workingdays = 366;\n})"
        );
    }
)}
2015-06-14 19:54:15.880 WorkingDays[6028:2219449] popToViewController:transition: called on <UINavigationController 0x7fc02c007e00> while an existing transition or presentation is occurring; the navigation stack will not be updated.

The modification of addContract() to fix the problem is as follow:

func addContract() {
    let appDelegate: AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
    let context: NSManagedObjectContext = appDelegate.managedObjectContext

    let entity = NSEntityDescription.entityForName("Contract", inManagedObjectContext: context)
    let newContractData = Contract(entity: entity!, insertIntoManagedObjectContext: context)
    do {
        newContractData.startdate = dateFormatter.dateFromString(startDateTextField.text!)!
        newContractData.enddate = dateFormatter.dateFromString(endDateTextField.text!)!
        newContractData.ship = shipNameTextField.text!
        newContractData.position = positionOnBoardTextField.text!
        newContractData.workingdays = Int(workingDaysLabel.text!)!

        try context.save()

    } catch {
        let alertController = UIAlertController(
            title: "Error",
            message: "The contract exsist",
            preferredStyle: UIAlertControllerStyle.Alert)
        let okAction = UIAlertAction(
            title: "OK",
            style: UIAlertActionStyle.Cancel,
            handler: nil)
        alertController.addAction(okAction)
        presentViewController(alertController, animated: true, completion: nil)

        context.deleteObject(newContractData)
        print(error)

    }
}
like image 301
nikolayDudrenov Avatar asked Jun 14 '15 10:06

nikolayDudrenov


1 Answers

Are you using a NSFetchedResultsController to show the data?

It seems that the uniqueness is ensured only when you save. But Core Data still allows you to insert the object into the NSManagedObjectContext when you do:

let newContractData = Contract(entity: entity!, insertIntoManagedObjectContext: context)

When you save, the save operations fails, but the object is still in the context, so the NSFetchedResultsController still displays it.

Try to remove the object from the context in your catch code:

context.deleteObject(newContractData)
like image 154
LombaX Avatar answered Sep 20 '22 23:09

LombaX