upbeat.it

by Cesare Rocchi

Upsert in Core Data

by Cesare Rocchi

I am a huge fan of upsert. An upsert operation creates a new record in a database, but if the record already exists it just updates it. All of that in the same query. Without upsert you’d need something like this (in pseudocode):

if (record.exists?) {
	// update code here
} else {
	// insert code here
}

The disadvantage of this is that you have to perform two queries, one to check the existence of the record and one either to update or to insert it. Is there something like upsert in Core Data? Yes! I discovered it recently.

I had to import a bunch of quotes from a JSON file. Each quote has an author property. One author, with the same name, can have more than one quote. But the JSON file was a simple array of quotes. Without upsert I’d end up with many authors with the same name. How to solve this? First you create a constraint on the name attribute.

Setting a constraint in Core Data

Second you specify the TrumpMergePolicy in the Core Data context.

managedObjectContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy

AFAIK, the name of the policy has nothing to do with the guy running for president in the US :)

The policy declares that merge operations occur on a property basis and the in memory version “wins” over the persisted one. Now I can import quotes without worrying about authors with the same name. I could even write code like this:

let author = NSEntityDescription.insertNewObjectForEntityForName("Author", inManagedObjectContext: managedContext)
author.name = "Cesare Rocchi"

let author2 = NSEntityDescription.insertNewObjectForEntityForName("Author", inManagedObjectContext: managedContext)
author2.name = "Cesare Rocchi"

do {
  try managedContext.save()
  } catch {
  let nserror = error as NSError
  NSLog("Unresolved error \(nserror), \(nserror.userInfo)")
  }

Once saved there will be only one Cesare Rocchi in the database. And that’s right, because the world is not ready for a “clone” :)

UPDATE: 2016/04/08 Obviously you should use an id field to guarantee uniqueness, but this method allows me to detect if there are trailing spaces in names or (even worse) authors with two different names, like “Steve Jobs” and “Steven P. Jobs”.

Thanks to @Socraz6 for pointing out that this works only in iOS9.