detect CoreData unique constraint violation

Viewed 12

I have a simple CoreData data model with:
name
timestamp
ident
where ident is a unique constraint.

With the following example code I try to insert a duplicate on the one hand and to make a duplicate out of an existing object on the other hand. As expected, an exception is thrown.

struct CV:View {
  @Environment(\.managedObjectContext) private var moc
  @FetchRequest(sortDescriptors: [])
  
  private var persons: FetchedResults<PersonCD>
  
  var body: some View {
    VStack{
      Button("add Person A"){addPerson(id: "A")}
      Button("set first Person A"){persons[0].ident = "A"}
      Button("save Context"){saveContext()}
    }
  }
  
  private func saveContext(){
    do {
      try moc.save()
    } catch {
      let nsError = error as NSError
      print(type(of: error))
      print("Unresolved error \(nsError), \(nsError.userInfo)")
      print("d:\(nsError.domain)")
      print("c: \(nsError.code)")
      print("ue: \(nsError.underlyingErrors)")
      print("cn \(nsError.className)")
      print("ds \(nsError.description)")
    }
  }
  
  private func addPerson(id:String) {
    let newPerson = PersonCD(context: moc)
    newPerson.timestamp = Date()
    newPerson.ident=id
  }
}

Two questions about this:

  • What is the easiest way to distinguish the exception from other possible exceptions and react to it (I can only see an NSError)?
  • Is it possible to get the information about which objects are in conflict, which is available in the description, as a reference to the duplicate objects?
1 Answers

This only causes an exception if you haven't set up a merge policy on your managed object context. The merge policy tells the context what to do if a conflict happens. If you don't set one up, the default policy is to throw an error.

Core Data has several built-in merge policies you can use, and most people find that one of these does the job they need. You would choose a merge policy with a line of code like

moc.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump

The built in policies are mergeByPropertyObjectTrump, mergeByPropertyStoreTrump, rollback, and overwrite (and of course error, which is what you have now). You can read about them in Apple's documentation.

If you want to handle it yourself, by getting details about the conflict, you can write your own merge policy and use that. To do that you'd create a subclass of NSMergePolicy like this:

class MyMergePolicy: NSMergePolicy {
    override func resolve(constraintConflicts list: [NSConstraintConflict]) throws {
        print("Constraint conflict list: \(list)")
    }
}

Then tell the context to use it with a line like this:

moc.mergePolicy = MyMergePolicy(merge: .errorMergePolicyType)

(You have to use one of the built-in merge policies here, you can't just use () to initialize it).

Now when a conflict happens, your custom merge policy will be called with an array of NSConstraintConflict objects. Those will tell you all about the conflict with details of the constraint that was violated and what values were used and on what objects.

Related