Search code examples
iosobjective-ccore-datansfetchrequest

NSFetchRequest for 2-way to-many Relationship


I recently started using CoreData and ran into a complicated (at least to me) fetch that I could not figure out. I searched around but had a hard time coming up with good terminology to search with, and thus had no results that sufficed. Here is a basic example of my coredata model:

enter image description here

What I am trying to do is execute a fetch to get all of the Persons that are a member of a particular Club. Example: get all of the members from the Chess Club. I could do this easily with SQL (have a third table that represents PersonInClub, do two joins to combine the two tables, and Select all the Persons Where Club.name = 'Chess Club') but I am struggling to implement this with a fetch request.

My ultimate goal is to write an NSFetchRequst for an NSFetchedRequestController. This is why I cannot just do a fetch for the Club with the name Chess Club and then grab all of the members (unless I am misunderstanding something, can you do this and put the results in a controller?).

Any insights are much appreciated!

EDIT: Here is the current code for my NSFetchedResultController. Not sure how this will help though:

- (NSFetchedResultsController *)fetchedResultsController
{
    if (_fetchedResultsController != nil) {
        return _fetchedResultsController;
    }

    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];

    [fetchRequest setEntity:[NSEntityDescription entityForName:@"Person" inManagedObjectContext:self.managedObjectContext]];


    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:NO];    
    [fetchRequest setSortDescriptors:@[sortDescriptor]];

    // This will give me the chess club but I want it's members!
    // I tried things like @"[self.name = %@ members]" and stuff to try and retrieve the members as the results
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"self.name = %@", @"Chess Club"];
    [fetchRequest setPredicate:predicate];

    // an array with just the Chess Club in it
    NSArray *results = [self.managedObjectContext executeFetchRequest:fetchRequest error:nil];

    // Printing the members, The members are correct but again, they are not attached to the Controller, the actual club is
    NSSet *members = [results.lastObject members];
    NSMutableArray *memberNames = [NSMutableArray arrayWithCapacity:members.count];
    for (Person *p in [members allObjects])
        [memberNames addObject:p.firstName];
    NSLog(@"All Members: %@", memberNames); // prints all of the members accurantely

    NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:@"Master"];
    aFetchedResultsController.delegate = self;
    self.fetchedResultsController = aFetchedResultsController;

    NSError *error = nil;
    if (![self.fetchedResultsController performFetch:&error]) {
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        abort();
    }

    return _fetchedResultsController;
}  

Solution

  • Well after far to long I eventually figured it out. It was actually quite simple, I used for following code (I have included extra code to help clarify the question and answer):

    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Person" inManagedObjectContext:self.managedObjectContext];
    [fetchRequest setEntity:entity];
    
    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"firstName" ascending:NO];    
    [fetchRequest setSortDescriptors:@[sortDescriptor]];
    
    // This is the important part
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"ANY self.clubs.name = %@", @"Chess Club"];
    //
    
    [fetchRequest setPredicate:predicate];
    
    // Just to clarify received data
    NSArray *allStudentsInChessClub = [self.managedObjectContext executeFetchRequest:fetchRequest error:nil];
    
    
    NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:@"Master"];
    aFetchedResultsController.delegate = self;
    
    // My NSFetchedResultsController with students from the Chess Club
    self.fetchedResultsController = aFetchedResultsController;
    

    I assumed this would not work because I thought it would be attempting to access the property name from the NSSet not the values in the set. This returned all of the of the members in the Chess Club, not the Chess Club itself!