问题
I am trying to set a constraint in core data with the new Entity Constraints inspector (To make the name of the item unique). All that I've read says it's pretty simple - Set the constraint and handle the error. I don't get any errors and can add the same entry as many times as I want.
The app does require IOS 9.0, Xcode tools requirement is set to 7.0
The constraint, category1Name, is a String.
My addItem code is:
func addNewRecord() {
//check to be sure the entry is not empty
if (categoryTextField.text == "") {
//prompt requiring a name
let ac = UIAlertController(title: nil, message: "Name Required", preferredStyle: .Alert)
ac.addAction(UIAlertAction(title: "Ok", style: .Default, handler: nil))
self.presentViewController(ac, animated: true, completion: nil)
} else {
let newManagedObject = NSEntityDescription.insertNewObjectForEntityForName("Category1", inManagedObjectContext: kAppDelegate.managedObjectContext) as! Category1
newManagedObject.category1Name = categoryTextField.text
newManagedObject.category1Description = categoryTextView.text
//bunch more items...
//save it
kAppDelegate.saveContext()
makeEntryFieldsEnabledNO()
performSegueWithIdentifier("unwindToCategoriesTableViewController", sender: self)
}//if else
}//addNewRecord
The AppDelegate save is standard:
func saveContext () {
if managedObjectContext.hasChanges {
do {
try managedObjectContext.save()
} catch {
//insert your standard error alert stuff here
let nserror = error as NSError
print("From the print line: Unresolved error \(nserror), \(nserror.userInfo)")
abort()
}//do catch
}//if moc
}//saveContext
Here's the Core Data constraint:
This app is iCloud enabled.
The managedObjectContext merge policy is set to NSMergeByPropertyObjectTrumpMergePolicy
lazy var managedObjectContext: NSManagedObjectContext = {
// Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.) This property is optional since there are legitimate error conditions that could cause the creation of the context to fail.
let coordinator = self.persistentStoreCoordinator
var managedObjectContext = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType)
managedObjectContext.persistentStoreCoordinator = coordinator
managedObjectContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
return managedObjectContext
}()//var managedObjectContext
Any guidance would be appreciated.
回答1:
If you want to get an error when there are merge conflicts and handle them manually then you need to change your policy to NSErrorMergePolicy and you will get an error and in the user info the object IDs that you need to solve merge conflict , otherwise it will merge and save according to the specified merge policy. The policy that you set will overwrite the object attributes but not relationships, if you want to overwrite the attributes and relationships then specify the NSMergeByPropertyObjectTrumpMergePolicy.
回答2:
The comment from pbasdf above seems to be correct. Constraints in the inspector don't save. I used both methods suggested in the link provided - I added the constraint, I changed another attribute, did a file save, then changed that attribute back and did a file save again. The constraint now acts as I expect. I'll mark this as answered. pbasdf should get the credit.
回答3:
CoreData in 2020...
It would appear Apple have finally fixed the insane Xcode problem where - not a joke - changes you make in a data model file don't actually change.
Putting that aside, the current formula seems to be:
in your core data singleton ...
container = NSPersistentContainer(name: _nom)
// during development, right HERE likely delete the sql database file
// and start fresh, as described here stackoverflow.com/a/60040554/294884
container.loadPersistentStores { storeDescription, error in
if let error = error {
print("\n ERROR LOADING STORES! \(error) \n")
}
else {
print("\n STORES LOADED! \(storeDescription) \n")
}
container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
container.viewContext.automaticallyMergesChangesFromParent = true
}
You must use merge policy and automatically merges.
Then in your data model file
- Don't bother unless every relationship has an inverse, with
- "to one or many" correctly set
- and (almost certainly, except in very source unusual data) your unique id for each entity is indicated as a constraint
Then when you add new data, you must
- use the new background context supplied by the handy core data function which does that
- so, never try to make your own separate thread
- double-check you have done (1) and (2) !
- when you do add a few entities, you must do that inside a
perform
- and when you have finished adding entities (ie on the new thread) you must while still in the perform ...
- do a performAndWait which does two things
- save the new items (on the new child thread), and then
- save the new items (on the main view thread)
- naturally for both 7 and 8, you have to check .hasChanges before saving
Easy right?
So something like
let pm = core.container.newBackgroundContext()
pm.perform {
for onePerson in someNewData {
... create your new CDPerson entity ...
}
pm.bake()
}
Note that the bake routine is within the perform block,
and it looks like this:
func bake() {
self.performAndWait {
if self.hasChanges {
do {
try self.save()
}
catch {
print("bake disaster type 1 \(error)")
}
}
// OPTIONALLY, SEE BELOW
if core.container.viewContext.hasChanges {
do {
try core.container.viewContext.save()
}
catch {
print("bake disaster type 2 \(error)")
}
}
// OPTIONALLY, SEE BELOW
}
}
To be clear, notice the pm.bake
... in the function bake()
, the self
in the first half is indeed that newBackgroundContext
which is created for the loop inside the perform.
Note that these days you don't even need to save to the main context
Nowadays automaticallyMergesChangesFromParent
seems to work perfectly, if you "do everything in the long list above".
• In the bake above, add a couple print lines to see what is saved to the viewContext. You'll see that nothing, at all, is ever saved. It's all done properly by the child/whatever relationships in the engine
• So in fact, in reality you can just omit that passage of the code. All you have to do is
func bake() {
self.performAndWait {
if self.hasChanges {
do {
try self.save()
}
catch {
print("bake disaster type 1 \(error)")
}
}
}
来源:https://stackoverflow.com/questions/37313675/core-data-entity-unique-constraint-does-not-work