1
votes

I have the following set up in core data: A Dish entity, with many ingredients, and a Ingredient entity with one dish.

I'm having trouble fetching my to-many relationship data, i.e. the ingredients for a certain dish. I'm trying to populate my collection view cells with the ingredients associated to each dish.

I have my core data model set up as follows:

Core Data model

I have tried various methods to fetch the data.

One of the things I've tried is to do the following: Dish *dish = [self.fetchedResultsController objectAtIndexPath:indexPath]; and then NSLog dish.ingredients (which returns a NSSet), but the returns the whole core data item, i.e., the createdAt date, the name and everything, together with the unformatted version of the ingredients for that dish.

Another thing i tried was to create a new instance of the Ingredient entity with Ingredient *ingredient = [dish.ingredients valueForKeyPath:@"ingredientName"];, but that returns an unformatted string (I want it UTF8 encoded before it's displayed because it contains special characters).

A lot of solutions when googling suggests using NSPredicate, but I just can't figure out how to use that. I'm still new to core data, and XCODE in general, so any help here would be appreciated, i.e. where do you implement the NSPredicate, and what would the correct predicate be in my case?

A bit of supporting code:

my collectionViewController:

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

    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    // Edit the entity name as appropriate.
    NSManagedObjectContext* context = [RKObjectManager sharedManager].managedObjectStore.mainQueueManagedObjectContext;
    self.managedObjectContext = context;
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Dish" inManagedObjectContext:self.managedObjectContext];
    [fetchRequest setEntity:entity];

    // Set the batch size to a suitable number.
    [fetchRequest setFetchBatchSize:20];

    // Edit the sort key as appropriate.

    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"createdAt" ascending:NO];
    NSArray *sortDescriptors = @[sortDescriptor];

    [fetchRequest setSortDescriptors:sortDescriptors];

    // Edit the section name key path and cache name if appropriate.
    // nil for section name key path means "no sections".
    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]) {
        // Replace this implementation with code to handle the error appropriately.
        // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        abort();
    }

    return _fetchedResultsController;
}

cellForItemAtIndexPath:

-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    FOFPhotoCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"photo" forIndexPath:indexPath];

    self.object = [self.fetchedResultsController objectAtIndexPath:indexPath];
    Dish *dish = [self.fetchedResultsController objectAtIndexPath:indexPath];

    Ingredient *ingredient = ???;
    cell.backgroundColor = [UIColor lightGrayColor];





    [cell.imageView setImageWithURL:[NSURL URLWithString:[[NSString stringWithFormat:@"http://192.168.0.3:4000%@", dish.dishImage] description]] completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType) {
    }];

    cell.titleLabel.text = dish.dishName;
    cell.headerLabel.text = dish.dishHeader;


    cell.ingredientsLabel.text = [NSString stringWithFormat:@"%@", nil];

    NSLog(@"%@", ingredient);


    [self.collectionView reloadItemsAtIndexPaths:[NSArray arrayWithObject:indexPath]];

    return cell;
}
2

2 Answers

0
votes

The simple way is your first approach and then only use the attributes that you care about from the ingredients. Core Data is more about programming with objects than it is about the underlying database, so attempting to simulate DB queries isn't usually the right approach.

I also recommend getting rid of all the valueForKey stuff and using actual object properties so that the code is a bit clearer and the compiler can help you out more with data types.

0
votes

If you're looking for Ingredients only for a one specific Dish, you have to set your fetchRequest for Ingredients (and not Dish) and set the predicate on Dish. To make the following code work, you may need first to Create NSManagedObject Subclasses (with the help of Xcode Editor button).

#import "Dish.h"
#import "Ingredient.h"

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

    NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Ingredient"];
    [fetchRequest setFetchBatchSize:20];

    //if dishname are unique values, set your predicate on it.
    //You can also set your predicate on the dishID if dishName are not unique values (several dishes with a same name)
    NSString *theDishIWant = @"Ratatouille"; //whatever Dish name you want
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"dish.dishName == %@", theDishIWant];
    [fetchRequest setPredicate:predicate];

    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"ingredientname" ascending:YES];
    [fetchRequest setSortDescriptors:@[sortDescriptor];

    NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:[RKObjectManager sharedManager].managedObjectStore.mainQueueManagedObjectContext sectionNameKeyPath:nil cacheName:nil];

    aFetchedResultsController.delegate = self;
    self.fetchedResultsController = aFetchedResultsController;

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

NSLog (@"%@", _fetchedResultsController);
return _fetchedResultsController;
}

Your configureCell: atIndexPath: method may look like this:

- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
    Ingredient *ingredient = [self.fetchedResultsController objectAtIndexPath:indexPath];

    cell.textLabel.text = ingredient.ingredientName;
    cell.detailTextLabel.text = ingredient.ingredientValue;
}