DrFloyd5
DrFloyd5

Reputation: 13787

How do I use the properties returned from a NSFetchRequest?

While working on an iOS app, I am having issue resolving the properties returned from a NSFetchRequest. This is not about setting the resultType or the propertiesToFetch. It is about using the NSArray of NSDictionary instances.

Here is the actual code below, the crash is near the bottom. Thank you! BTW, the point of this code is to eventually produce a list of section headers based on hair color (that is not under hats) and then produce a list of people, without hats who have that hair color for the cells. I am not sure this is the right approach to do that, but regardless, the question stands. Thanks Again!

//
//  CDHairbrained.m
//

#import "CDHairbrained.h"
#import "CDHair.h"
#import "CDPerson.h"

@implementation CDHairbrained

void defaultErrorBlock(NSError*error) {
     NSLog(@"Failed to save to data store: %@", [error localizedDescription]);
     NSArray* detailedErrors = [[error userInfo] objectForKey:NSDetailedErrorsKey];
     if(detailedErrors != nil && [detailedErrors count] > 0) {
          for(NSError* detailedError in detailedErrors) {
                NSLog(@"  DetailedError: %@", [detailedError userInfo]);
          }
     } else {
          NSLog(@"  %@", [error userInfo]);
     }
     UIAlertView* av = [[UIAlertView alloc] initWithTitle:@"Booo..." message:@"MangagedObjectContext Error" delegate:nil cancelButtonTitle:@"cry" otherButtonTitles: nil];
     [av show];
}

-(void) initializeObjectContext:(NSManagedObjectContext*)moc {
     //NSArray<CDHairs>
     for (CDHair *hair in [self fetchAllOfEntityName:@"Hair" InManagedObjectContext:moc]) {
          [moc deleteObject:hair];
     }
     for (CDPerson *person in [self fetchAllOfEntityName:@"Person" InManagedObjectContext:moc]) {
          [moc deleteObject:person];
     }

     //NSDictionary{color}
     NSDictionary* hairdata = @{@"red": [NSEntityDescription insertNewObjectForEntityForName:@"Hair" inManagedObjectContext:moc],
                                         @"blond":[NSEntityDescription insertNewObjectForEntityForName:@"Hair" inManagedObjectContext:moc],
                                         @"brown":[NSEntityDescription insertNewObjectForEntityForName:@"Hair" inManagedObjectContext:moc],
                                         @"black":[NSEntityDescription insertNewObjectForEntityForName:@"Hair" inManagedObjectContext:moc]
                                         };
     for (NSString* color in hairdata.allKeys) {
          CDHair* hair = hairdata[color];
          hair.color = color;
     }

     //NSArray<NSDictionary{name,hair,hat}>
     NSArray* peopleData = @[
                                     @{@"name":@"Stan",@"hair":hairdata[@"red"], @"hat":@"no"},
                                     @{@"name":@"Lucy",@"hair":hairdata[@"red"], @"hat":@"no"},

                                     @{@"name":@"Fred",@"hair":hairdata[@"black"], @"hat":@"no"},
                                     @{@"name":@"Sherlock",@"hair":hairdata[@"black"], @"hat":@"yes"},

                                     @{@"name":@"Barney",@"hair":hairdata[@"blond"], @"hat":@"yes"},
                                     @{@"name":@"Dennis",@"hair":hairdata[@"blond"], @"hat":@"yes"}

                                     ];
     for (NSDictionary* personData in peopleData) {
          CDPerson* person =[NSEntityDescription insertNewObjectForEntityForName:@"Person" inManagedObjectContext:moc];
          person.name = personData[@"name"];
          person.hair = personData[@"hair"];
          person.hat = personData[@"hat"];
     }
     NSError*error;
     [moc save:&error];
     if(error) defaultErrorBlock(error);
}

-(NSArray*) fetchAllOfEntityName:(NSString*)entityName InManagedObjectContext:(NSManagedObjectContext*) moc {
     NSFetchRequest* request = [NSFetchRequest fetchRequestWithEntityName:entityName];
     NSError* error;
     NSArray* fetchResults = [moc executeFetchRequest:request error:&error];
     if (fetchResults) {
          return fetchResults;
     }
     defaultErrorBlock(error);
     return nil;
}

-(NSArray*) fetchDistinctProperties:(NSArray*) propertyDescriptors
                             forEntityName:(NSString*) entityName
                                  Predicate:(NSPredicate*) predicate
                                    SortedBy:(NSArray*) sortDescriptors
                 InManagedObjectContext:(NSManagedObjectContext*)moc
                              FailureBlock:(void(^)(NSError*)) failureBlock
{
     // The darnedest thing: you can't query disctict against in memory changes.
     // CoreData is more trouble than it is worth.
     if (moc.hasChanges) {
          [NSException raise:@"FetchDistinct not return in memory changes." format:@"%@ has unsaved changes.",moc];
     }
     NSFetchRequest* fetchRequest = [NSFetchRequest fetchRequestWithEntityName:entityName];
     fetchRequest.returnsDistinctResults = YES;
     fetchRequest.propertiesToFetch = propertyDescriptors;
     fetchRequest.resultType =NSDictionaryResultType;
     fetchRequest.predicate=predicate;
     fetchRequest.sortDescriptors = sortDescriptors;

     NSError* error;
     NSArray* fetchResults = [moc executeFetchRequest:fetchRequest error:&error];
     if (fetchResults) {
          NSLog(@"Fetched %3lu properties of %@", (unsigned long)fetchResults.count, entityName );
          return fetchResults;
     }
     if (failureBlock)
          failureBlock(error);
     else
          defaultErrorBlock(error);
     return nil;
}

-(void) doIt:(NSManagedObjectContext*)moc {
     [self initializeObjectContext:moc];

//     Get a list of distinct Hair that is not underhats, to be section headers.
//     And Get a list of People, with that Hair and without hats to be section cells.
//     
//     Expecting visibleHair to contain red, black. Not blond (none visible) Not brown, no people w/ brown hair.


     // Get a distinct list of hair properties from all people without hats.
     // Presume result is NSArray*<NSDictionary*{"hair":CDHair*}>
     NSArray* visibleHair = [self fetchDistinctProperties:@[@"hair"]
                                                                  forEntityName:@"Person"
                                                                        Predicate:[NSPredicate predicateWithFormat:@"hat=='no'"]
                                                                         SortedBy:nil
                                                      InManagedObjectContext:moc
                                                                    FailureBlock:nil
                                              ];
     // Quick Sanity Check for the debugger
     NSDictionary* foundProperties = [visibleHair firstObject];
     CDHair* aFoundHair = foundProperties[@"hair"];
     NSLog(@"%u",aFoundHair.isFault); // <--- is nil
     NSLog(@"aFoundHair: %@",aFoundHair);
     NSLog(@"aFoundHair: %@",aFoundHair.color); // <------ CRASH!
                                                              // 2013-11-06 12:43:19.513 CDTest[2865:70b] -[_NSObjectID_48_0 color]: unrecognized selector sent to instance 0x8ba8670
     NSLog(@"aFoundHair: %@",aFoundHair);

     // Get a list of people with a particular Hair Color, who don't have hats.
     NSSet* peopleWithAFoundHair = aFoundHair.people; // of CDPerson
     NSArray* peopleWithAFoundHairSorted=[peopleWithAFoundHair sortedArrayUsingDescriptors:
                                                                      [NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES]
                                                      ]; // of CDPerson
     NSArray*peopleWithAFoundVisibleHairSorted = [peopleWithAFoundHairSorted filteredArrayUsingPredicate:
                                                                 [NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings)
                                                                  {
                                                                        CDPerson*p=evaluatedObject;
                                                                        return [p.hat compare:@"no"]==NSOrderedSame;
                                                                  }]
                                                                 ]; // of CDPerson
     CDPerson* aPerson=[peopleWithAFoundVisibleHairSorted firstObject];
     NSLog(@"%@",aPerson);
}


@end

Upvotes: 0

Views: 93

Answers (2)

Mundi
Mundi

Reputation: 80265

Your fetchDistinctProperties: method needs an array of NSPropertyDescriptions but you are passing an array of NSString.

Upvotes: 0

Mundi
Mundi

Reputation: 80265

The NSDictionaryResultType returns an array of dictionaries with property names and values, not an array of dictionaries with entity names and values.

Thus, not:

[ 
  { "person" : PersonObject },
  { "person" : OtherPersonObject }
]

but rather

[
  { "name" : "John", "age"  : 30 },
  { "name" : "Jane", "age"  : 20 }
]

To do what you want, you need to just fetch the CDPerson entity with NSManagedObjectResultsType.

Person *person = fetchedObjects[0];
NSLog (@"%@", person.name);

Note that "Person.name" (with a capital "P") is probably wrong, as it looks like a class method rather than an instance method.

Upvotes: 1

Related Questions