epologee
epologee

Reputation: 11319

Find or create unique Core Data entity

A common scenario for core data driven apps is getting a unique object out of the backing store. If the object with a certain unique property exists, return that one, if it doesn't return a newly create one. I found myself writing the same thing over and over, so I wrapped it in a convenience method. But this seems so trivial, am I reinventing the wheel here? Is there a simpler, out-of-the-box way to achieve this?

Cheers,
EP

+(id)uniqueEntityfForName:(NSString *)name 
                withValue:(id)value 
                   forKey:(NSString *)key
   inManagedObjectContext:(NSManagedObjectContext *)context {

    NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];    
    request.entity = [NSEntityDescription entityForName:name inManagedObjectContext:context];
    request.predicate = [NSPredicate predicateWithFormat:[key stringByAppendingString:@" == %@"], value];
    NSArray *result = [context executeFetchRequest:request error:nil];

    id entity = [result lastObject];
    if (entity == nil) {
        entity = [NSEntityDescription insertNewObjectForEntityForName:name inManagedObjectContext:context];
        [entity setValue:value forKey:key];
    } else {
        entity = [result lastObject];
    }

    return entity;
}

I use this method like this:

SomeEntity *entity = [CDUtils uniqueEntityfForName:@"SomeEntity" withValue:@"foo" forKey:@"bar" inManagedObjectContext:context];

Upvotes: 13

Views: 4864

Answers (2)

docchang
docchang

Reputation: 1125

A more flexible solution is to use Blocks to let the caller to handle 3 situations when comparing two lists.

  1. Matching Sets
  2. Host unmatched sets
  3. Local unmatched sets

Therefore, there is no need to create similar functions when inserting in a syncing fashion or additions to the data store.

typedef void (^objectOperationBlock)(NSManagedObjectContext *context,
                                 NSDictionary *hostObjectData,
                                 NSManagedObject *localManagedObject);

- (void) insertUniquely:(NSArray *)rawDataArray 
                 entity:(NSString *)entity 
           matchedBlock:(objectOperationBlock)matchedOperation
     hostUnmatchedBlock:(objectOperationBlock)hostUnmatchedOperation
    localUnmatchedBlock:(objectOperationBlock)localUnmatchedOperation
                  error:(NSError **)outError;

A full implementation can be found here: http://emplementation.blogspot.com/2011/12/importing-data-into-core-data-while.html

Upvotes: 2

rgeorge
rgeorge

Reputation: 7327

Pretty standard. My core data entities have lots of methods like [aStudent enrollmentForId:(long long)idValue createIfMissing:YES].

I'd also like to plug mogenerator , which removes much pain from Core Data. Among other things, it generates a factory method for every fetch request defined in the data model. So making a fetch predicate in the model like, e.g,

thingies: thingyId == $forThingyId

yields a matching class method:

+(NSArray *)fetchThingies:(NSManagedObjectContext *)moc forThingyId:(id)thingyId

...which does the first half of what you've written up there. A wrapper like

-(Thingy*)thingyForIdValue:(long long)thingyId

is then trivial to write, in whatever class holds your managedObjectContext (eg. a "parent" entity, or app delegate, or whatever.)

Upvotes: 3

Related Questions