WatchKit Extension Problem: Sharing a Core Data Store Can Lead to Duplicate Entries

The WatchKit SDK was released yesterday, emphasizing an interesting problem that affects all iOS 8 extensions that need to share a Core Data store with their host app. Consider this scenario:

General Setup

Sequence of Events Leading to the Problem

  1. The user launches the host app. It begins fetching data from the remote API resource. Core data model objects are created based on the API response and “upserted” into the host app’s managed object context. Each API entity has a uniqueID that identifies it in the remote API backend. By “upsert,” I mean that for each API entity, the host app only creates a new entry in Core Data if an existing entry for a given uniqueID cannot be found.

  2. Meanwhile, the user also launches one of the host app’s extensions. It, too, performs some kind of fetch from the same remote API. It also attempts to perform an “upsert” when parsing the API responses.

  3. The Problem: What happens if both the host app and an extension try to upsert a Core Data entry for the same API entity at the same time? To see how this could come about, let’s look at the sequence of events for an upsert:

Core Data Upsert Sequence

  1. The API parsing code parses the uniqueID for a given API entity.

  2. The parser performs a Core Data fetch for any entry that matches a predicate where uniqueID is equal to the parsed uniqueID.

  3. If an existing entry is not found, the parser inserts a new Core Data entry for this API entity, set’s its uniqueID attribute to the parsed uniqueID.

  4. The parser saves the managed object context, which pushes the new entry data down to the SQLite backing store.

Problem in Detail

Let’s assume the host app and the extension are independently parsing an API response for the same API entity at the same time. If both the host app and an extension reach Step 3 before either of them has finished Step 4, then they will both be trying to insert a new Core Data entry for the same uniqueID. When they reach Step 4 and call save: on their respective managed object contexts, Core Data will happily create duplicate entries.

As far as I’m aware, Core Data doesn’t have any way to mark an attribute as unique. I need a Core Data equivalent to a SQLite INSERT OR IGNORE + UPDATE combo.. Or else I need a way to “lock” the persistent store’s SQLite backing store, which sounds like a recipe for trouble.

Is there a known approach to this rather novel problem introduced by iOS 8 extensions? If you have an answer, please consider posting it on Stack Overflow.

|  19 Nov 2014