CoreData generic findManyByKey

Wed, 09. Dec 2009

Categories: en development Tags: Cocoa CoreData iPhone NSEntityDescription NSManagedObject NSManagedObjectContext NSPredicate Objective C

The base for many of my SELECT-ish queries when querying by exact match is one generic method I created in some category methods on NSManagedObjectContext:

 1-(NSArray*)entityName:(NSString*)entityName findManyByRelation:(NSDictionary*)dict
 2{
 3//  TODO handle dict nil and emptyness
 4    NSMutableArray *arr = [[NSMutableArray alloc] initWithCapacity:dict.count];
 5    for(NSString *key in dict)
 6    {
 7        NSExpression *left = [NSExpression expressionForKeyPath:key];
 8        NSExpression *right = [NSExpression expressionForConstantValue:[dict objectForKey:key]];
 9        NSComparisonPredicate *cp = [[NSComparisonPredicate alloc] initWithLeftExpression:left
10            rightExpression:right modifier:NSDirectPredicateModifier type:NSEqualToPredicateOperatorType options:0];
11        [arr addObject:cp];
12        [cp release];
13    }
14    NSPredicate *pred = nil;
15    if(arr.count == 1)
16        pred = [[arr objectAtIndex:0] retain];    // why do I have to retain here?
17    else
18        pred = [[NSCompoundPredicate alloc] initWithType:NSAndPredicateType subpredicates:arr];
19
20    NSFetchRequest *fr = [[NSFetchRequest alloc] init];
21    fr.entity = [NSEntityDescription entityForName:entityName inManagedObjectContext:self];
22    fr.predicate = pred;
23    [arr release];
24    [pred release];
25//  NSLog(@"predicate effective: %@", fr.predicate);
26
27    NSError *err = nil;
28    NSArray *ps = [self executeFetchRequest:fr error:&err];
29    if(err != nil)
30        [NSException raise:@"DB Error" format:@"Fetch problem %@", fr.predicate.predicateFormat];
31    [fr release];
32    return ps;
33}

For example it serves under the hood of it’s sibling helpers

 1-(NSManagedObject*)entityName:(NSString*)entityName findByPrimaryKey:(NSDictionary*)dict
 2{
 3    NSArray *ps = [self entityName:entityName findManyByRelation:dict];
 4    if(ps == nil || ps.count == 0)
 5        return nil;
 6    if (ps.count == 1)
 7        return [ps objectAtIndex:0];
 8    [NSException raise:@"DB Error" format:@"Multiple hits for %@", dict];
 9    return (NSManagedObject*)1/0;
10}

and

 1-(NSManagedObject*)entityName:(NSString*)entityName selectOrInsertWithKey:(NSDictionary*)dict
 2{
 3    NSManagedObject *o = [self entityName:entityName findByPrimaryKey:dict];
 4    if(o == nil)
 5    {
 6        NSEntityDescription *ed = [NSEntityDescription entityForName:entityName inManagedObjectContext:self];
 7        o = [[NSManagedObject alloc] initWithEntity:ed insertIntoManagedObjectContext:self];
 8        for(NSString *key in dict)
 9            [o setValue:[dict objectForKey:key] forKey:key];
10        [o autorelease];
11    }
12    return o;
13}

Those methods again are called by custom convenience wrappers in the model classes loosely following the ActiveRecord Pattern.