Skip to content

Instantly share code, notes, and snippets.

@nonsensery
Last active August 29, 2015 13:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nonsensery/9159991 to your computer and use it in GitHub Desktop.
Save nonsensery/9159991 to your computer and use it in GitHub Desktop.
Creating singleton records

Here's a pattern I've used for creating unique "singleton" records.

Given a desired tag name, name, and managed object context, context,

  1. Using context, fetch all Tag objects with the desired name, ordered ascending by primary key.
  2. If there is more than one result, return the first one. You're done.
  3. Otherwise, create a new, temporary, (peer) context, tempContext.
  4. Using the temporary context,
  5. Create and insert a new Tag with the desired name, newTag, and save changes.
  6. Fetch all Tag objects with the desired name, ordered ascending by primary key (just like step 1, but using tempContext).
  7. The first result is the actual unique record, tag.
  8. If the tag is not the newTag you created, that means someone else beat you to it. Delete newTag and save changes.
  9. Get a local instance of the tag in context and return it.

The nice thing about this pattern is that it uses the underlying database as the synchronization mechanism, which allows multiple threads to create unique Tag objects without needing to coordinate with each other at the thread level.

Here's a rough, hand-written implementation of that pattern using Objective-C and Core Data, with some hand waving to skip over the nitty-gritty, and no error handling.

A caveat: I've only ever used this pattern with EOF, which is a close cousin of Core Data. It depends on being able to order fetched rows by an auto-incrementing primary key.

+ (Tag *)tagWithName:(NSString *)name inContext:(NSManagedObjectContext *)context
{
  // `+fetchAllWithName:inContext:` fetches all instances of the entity
  // by name in the context, ordered ascending by primary key.
  
  NSArray *tags = [Tag fetchAllWithName: name inContext: context];
  
  if ([tags count]) {
    return [tags objectAtIndex:0];
  }
  
  // The tag doesn't already exist. The race is on to create the One True Instance.
  
  Tag *tag;
  
  NSManagedObjectContext *tempContext = [NSManagedObjectContext new];
  [tempContext setPersistentStoreCoordinator: [context persistentStoreCoordinator]];
  
  // Create a new instance:
  
  Tag *newTag = [NSEntityDescription insertNewObjectForEntityForName:@"Tag" inManagedObjectContext:tempContext];
  newTag.tagName = name;

  [tempContext save:nil];
  
  // Did we win the race?
  
  tags = [Tag fetchAllWithName: name inContext: tempContext];
  tag = [tags objectAtIndex:0];
  
  if (tag != newTag]) {
    // Oops, we lost the race, delete our tag:
    
    [tempContext deleteObject: newTag];
    [tempContext save:nil];
    
    newTag = nil;
  }
  
  // Get a "local" instance in the original context:
  
  return [context objectWithId: [tag objectID]];
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment