61

I use Xcode for iOS development. I have some entity (for example, User), and I need to set unique constraint for his name, but I can't find how I can do it through visual editor. Is it possible to do it through GUI? Or it's possible through code only? I will be glad to get some screenshot.

Cœur
  • 37,241
  • 25
  • 195
  • 267
malcoauri
  • 11,904
  • 28
  • 82
  • 137

3 Answers3

127

There's a new section in the sidebar when selecting an entity in the editor for Core Data. You can set what constraint(s) you want to be unique across all instances of an entity

For automatic conflict resolution during saves, you'll need to make sure you've got a merge policy set for your managed object context or else you'll just get errors when saving (which might actually be what you want)

[managedObjectContext setMergePolicy:NSMergeByPropertyObjectTrumpMergePolicy];

The "Swift version" is exactly the same

managedObjectContext.mergePolicy = .mergeByPropertyObjectTrumpMergePolicyType

Keep in mind conflict resolution only happens during saves, and not inserts. So if you're making use of a NSFetchedResultsController you will see entities with non-unique constraints as they're inserted.

enter image description here

If you want to sure you have no entities with non-unique constraints in your managed object context without saving (if you're making use of a FRC), this answer is still probably the best way to go. Although, keep in mind, it's expensive if you're doing a lot of inserts, since NSFetchRequests are expensive operations.

Sample code for this demo can be found here

Zachary Orr
  • 1,724
  • 1
  • 15
  • 25
  • I have added a property name to the comma-separated list but git status says no changes to my working directory have been made. – Skyler Mar 14 '16 at 18:50
  • 1
    @ZacharyOrr What if the app do try to insert a column that has "unique" constraint? Surely it will fail. What is the best way to avoid this? Query the DB first to find that record? Is it possible to use the "guard" statement (if Swift) when inserting the data? – Felipe Caldas Apr 26 '16 at 13:19
  • 6
    Keep in mind that unique constraints must be set only for `String` type. – Bartłomiej Semańczyk Jun 03 '16 at 07:16
  • 1
    Its not working for me. I added the constraint in Data Model Inspector and also set the NSMergeByPropertyStoreTrumpMergePolicy to my main and temp context. I am using parent/child context. I also set the `fetchRequest.returnsDistinctValues = true` and then executing the request using fetchedResultsController. But I am still not able to avoid addition of duplicate data and displaying unique data. I tried deleting and reinstalling the app from simulator. – Yash Tamakuwala Jul 05 '16 at 12:24
  • Good quality answer - but a link to the NSMergePolicy docs would have been nice. In my case I had to set the latest tools version (a bit surprisingly, "Automatic (Xcode 8)" from "Xcode 7") on the model before it would work properly, and immediately had an automatic migration failure. – Jim Driscoll Nov 25 '16 at 09:53
  • @FelipeCaldas I have the same question as you do. How did you resolve that issue? – KMC Mar 20 '17 at 16:04
  • 1
    can i make a unique constraint say for example BOTH constraints must be met in order to overwrite? Ex: UserId & objectId must match or else it is not overwritten? – Jerland2 Jul 01 '17 at 14:46
  • @Jerland2 I would recommend making a transient property in your model, and uniquing off that. You could, say, implement this as a computed property in a subclass of your object, and just concat together your two properties as strings. I don't know if the merge policy looks at transient properties, but it's worth a shot! – AverageHelper Feb 09 '20 at 15:06
  • Also, for you Swift folks out there, it seems that `NSMergePolicy.mergeByPropertyObjectTrump` works just as well as `NSMergeByPropertyObjectTrumpMergePolicy`. At least, the documentation is the same. – AverageHelper Feb 09 '20 at 15:08
14

Swift solution:

As noted in the other answer, you can have unique constraints in Core Data for iOS9 onwards.

To do this, first add constraints to the Entity from the Core Data Editor (explaination in Zachary's answer).

Then add this line in code:

managedObjectContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy

Note: The uniqueness is checked when you do managedObjectContext.save() not while you're just adding objects to the managed object.

NSMergeByPropertyObjectTrumpMergePolicy is just one of the merge policies, which overwrites the old value with the new one. You might want to check the documentation for other options.

Nitin Nain
  • 5,113
  • 1
  • 37
  • 51
  • 1
    This doesn't show an error if the record already exists, how to improve UX with error? – Takasur Dec 11 '18 at 10:59
  • 3
    UX as in you want to tell the user about some duplicate? Probably use NSErrorMergePolicy https://developer.apple.com/documentation/coredata/nserrormergepolicy . Catch the return value and then perform your custom logic. – Nitin Nain Dec 11 '18 at 11:55
0

swift Version Is Easy Disclaimer : If You Have Conflicting information please delete piror to implementation. Else App will not run. Solution Delete from device and start again

steps are:

  1. Open Core Data File (projectname.xcdatamodeld)
  2. Click on the entity name (needs to be highlighted)
  3. Right Side of screen (in class section) find constraints (hit the plus button)
  4. Right click to edit info rename to Attribute.

// now to add the code in you core data container

  1. open AppDelegate.swift file and scroll into the coredata stack (" // MARK: - Core Data Saving support ")
  2. update the code for the static func saveContext() { let variable = persistentContainer.viewContext "

//now make this simple call that manages update process

variable".mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy"

// clear understanding

static func saveContext () {

    let context = persistentContainer.viewContext

    context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
    // you need that line

    if context.hasChanges {
        do {
          try context.save()
        } catch {
Fattie
  • 27,874
  • 70
  • 431
  • 719