2

For some reason I only recently found out about unique constraints for Core Data. It looks way cleaner than the alternative (doing a fetch first, then inserting the missing entities in the designated context) so I decided to refactor all my existing persistence code.

If I got it right, the gist of it is to always insert a new entity, and, as longs as I have a proper merge policy, saving the context will take care of the uniqueness and in a more efficient way. The problem is every time I save a context with the inserted entity I get a NSCoreDataConstraintViolationException, no error though. When I do the fetch to make sure

  1. there is indeed only one instance with a unique field
  2. other changes to this entity were applied

everything seems to be okay, but I’m still concerned about this exception, since I do saves and therefore get it quite often, a few times per second in some cases.

My project is in objective-c and I know exceptions are expensive there so I’m having doubts if I’m missing something.

Here is a sample project with this issue (just a few lines of code, be sure to add an exception breakpoint)

dariaa
  • 6,285
  • 4
  • 42
  • 58

1 Answers1

0

NSMergeByPropertyObjectTrumpMergePolicy and constraints are not useful tools and should also never be used. The correct way to manage uniqueness is with a fetch before the insert as it appears you have already been doing.

Let's starts with why the only correct merge policy is NSErrorMergePolicy. You should only be writing to core data in on synchronous say (performBackgroundTask is not enough you also need an operation queue). If you have two performBackgroundTask running at the same time and they contradict then you will lose data. Merge policy is answer the question of "which data would you like to lose?" the correct answer is "Don't lose my data!" which is NSErrorMergePolicy.

The same issue happens when you have a constraint. Let's says you have an entity with a unique constraint on the phone number. And you try to insert another entity with the same phone number. What would you like to happen? It depends on what exactly the data is. It might be two different people, and the phone number should be made different (perhaps they were lacking area code), or it might be one person and the data should be merged. Or you might have a constraint on an uniqueID and the number should just be incremented. But on the database level it doesn't know. It always just does a merge. It will silently lose data.

You can create a custom NSMergePolicy and inspect NSConstraintConflict to decide what do to. But in practice you'd have to think about every time you edit the database and what each change means, which can be very hard outside of the context of writing a change to the database. In other words, the problem with a constraints and merge policy is that it the run is on the wrong level of your application to effectively deal with the problem.

Using constraints with a merge policy of error is OK, as it is a way to find problems with your app (as long as you are monitoring crashes and fixing them). But you still need to do the fetch before the insert to make sure the error doesn't happen.

If you want to clean up code then just have one place that you create your objects. Something like objectWithId:createIfNeed:inContext: which does the fetch and create.

Jon Rose
  • 8,373
  • 1
  • 30
  • 36
  • 1
    While I agree, I wouldn't generalize it in this way. It really depends on the context, how are you utilizing it, if you're using serial queues, ... I mean - every tool can be used in a wrong way and in a good way. It's always crucial to understand what the tool does and potential problems one should be aware of. – zrzka Jul 02 '20 at 11:02
  • 1
    Using a merge policy and constraints requires you to know on a _global_ level what conflicts may be created a how you want them resolved. This is a harder problem then knowing what conflict may be created by a single edit to your database. Constraints with merge policy makes the problem harder not easier. Of course you can solve the problem the harder way - but why would you want to? – Jon Rose Jul 06 '20 at 05:51
  • Thank you for your answer and comments, but they do not exactly answer my question. At this stage I would like to know, if it these warnings are just an internal core data mechanism to handle constraints conflicts and, most importantly, if they have any performance implications. – dariaa Jul 07 '20 at 13:02
  • 1
    Use Instruments and measure it. This is the only valid answer. – zrzka Jul 07 '20 at 14:01