Chuck Chuck - 3 months ago 12
Objective-C Question

How to prepareForSegue with NSFetchedResults drilldown to another tableview with objective c

I know there is probably a very simple solution to this. I'm using the apple iphoneCoreDataRecipes example. I'm trying to drill down from an initial table(RecipeTypeTableViewController) of RecipeTypes (ie Beverage, Dessert, Entree) to the next tableview with listing of those type of Recipes(RecipeListTableViewController). I created a new class RecipeType, thinking I could just use this to return recipes of a selected RecipeType. So far the best I can get is allowing selection but it is showing the same list of all recipes. The original code starts with RecipeListTableViewController using NSFetchedResults to show all recipes. Now I have the initial view of RecipeTypes but I'm not sure how to pass the selected type to RecipeListTableViewController. I need to change RecipeListTableViewController to show list of the selected RecipeType but I'm not sure how to implement this. I think I basically want to pass an NSMutableArray of fetchedresults to another tableview but not sure which direction to take. And I don't want to mess up other working actions in the RecipeListTableViewController, ie search and editing of recipes. Here's images of table action so far RecipeTypeTableView & RecipeListTableview. Thank you in advance for any suggestions.

File: RecipeType.h

#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>

@class Recipe;

@interface RecipeType : NSManagedObject
@property (nonatomic, strong) NSManagedObject *type;
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) Recipe *recipe;

@end


File: RecipeType.m

#import "RecipeType.h"
#import "Recipe.h"

@implementation RecipeType

@dynamic name, type, recipe;

@end


File: RecipeTypeTableViewController.h

// RecipeTypeTableViewController.h
// Recipes
#import <UIKit/UIKit.h>
@class Recipe, RecipeType;
@interface RecipeTypeTableViewController : UITableViewController <NSFetchedResultsControllerDelegate>
@property (strong, nonatomic) NSManagedObjectContext *managedObjectContext;
@property (nonatomic, strong) RecipeType *recipeType;
@property (nonatomic, strong) Recipe *recipe;
@end


File: RecipeTypeTableViewController.m in prepareForSegue(I show commented code of other tries)

#import "RecipeTypeTableViewController.h"
#import "Recipe.h"
#import "RecipeListTableViewController.h"
#import "RecipeType.h"


@interface RecipeTypeTableViewController () <NSFetchedResultsControllerDelegate>


@property (nonatomic, strong) NSFetchedResultsController *fetchedResultsController;
//@property (nonatomic, strong) NSArray *recipeTypes;
@property (strong, nonatomic) NSIndexPath *selection;

@end

@implementation RecipeTypeTableViewController

static NSString *MyIdentifier = @"showRecipes";

- (void)viewDidLoad {
[super viewDidLoad];
// register this class for our cell to this table view under the specified identifier 'ARecipeType'
self.title = @"Category";
//[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"ARecipeType"];

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didChangePreferredContentSize:)name:UIContentSizeCategoryDidChangeNotification object:nil];
// Uncomment the following line to preserve selection between presentations.
// self.clearsSelectionOnViewWillAppear = NO;

// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
NSError *error = nil;
if (![[self fetchedResultsController] performFetch:&error]) {

NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
//abort();
}
}

- (void)viewWillAppear:(BOOL)animated {

[super viewWillAppear:animated];
self.title = @"Category";

}

//- (void)viewDidLoad {

//[super viewDidLoad];

//id delegate = [[UIApplication sharedApplication] delegate];
//self.managedObjectContext = [delegate managedObjectContext];

// register this class for our cell to this table view under the specified identifier 'MyIdentifier'
//[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:MyIdentifier];
//}


- (void)didChangePreferredContentSize:(NSNotification *)notification
{
[self.tableView reloadData];
}

- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}

- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIContentSizeCategoryDidChangeNotification object:nil];
}
#pragma mark - Table view data source

//- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
//#warning Incomplete implementation, return the number of sections
//return [[self.recipeTypeFetchedResultsController sections] count];

// return 1;
//}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Number of rows is the number of recipe types
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section];
return [sectionInfo numberOfObjects];
//return self.recipeTypes.count;
}


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"ARecipeType" forIndexPath:indexPath];

// Configure the cell
//RecipeType *recipeType = (RecipeType *)[self.recipeTypes objectAtIndex:indexPath.row];
//cell.textLabel.text = [recipeType valueForKey:@"name"];
//cell.recipeType = recipeType;

//NSManagedObject *recipeType = [self.recipeTypes objectAtIndex:indexPath.row];
//cell.textLabel.text = [recipeType valueForKey:@"name"];

RecipeType *recipeType = nil;
recipeType = [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = [recipeType valueForKey:@"name"];

return cell;
}


#pragma mark - Fetched results controller

- (NSFetchedResultsController *)fetchedResultsController {

// Set up the fetched results controller if needed.
if (_fetchedResultsController == nil) {
// Create the fetch request for the entity.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:@"RecipeType" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
NSLog(@"Setting up a Fetched Results Controller for the Entity named %@", entity);

// Edit the sort key as appropriate.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];

[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:@"Recipe"];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;


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

return _fetchedResultsController;
}

#pragma mark - Navigation

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {

if ([[segue identifier] isEqualToString:@"showRecipes"]) {
NSLog(@"Setting RecipeType as a delegate of RecipeListTableViewController");
//if ([segue.destinationViewController isKindOfClass:[RecipeListTableViewController class]]) {
// Configure RecipeList View Controller
//[(RecipeListTableViewController *)segue.destinationViewController setRecipe:self.recipe];

// Reset Recipe
//[self setRecipe:nil];
//}
//RecipeListTableViewController *recipeListViewController = [segue destinationViewController];
//NSIndexPath *indexPath = (NSIndexPath *)sender;
//Recipe *recipe = (Recipe *)[self.fetchedResultsController objectAtIndexPath:indexPath];
//recipeListViewController.recipe = recipe;

RecipeListTableViewController *recipeListViewController = segue.destinationViewController;
//recipeListViewController.delegate = self;
recipeListViewController.managedObjectContext = self.managedObjectContext;

// Store selected Recipes in selectedRecipeType property
//NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
//self.recipe.type = [self.fetchedResultsController objectAtIndexPath:indexPath];

NSLog(@"Passing selected Recipe (%@) to RecipeListTableViewController", self.recipeType);
[recipeListViewController setManagedObjectContext:self.managedObjectContext];

if (self.selection) {
// Fetch Record
NSManagedObject *recipes = [self.fetchedResultsController objectAtIndexPath:self.selection];

if (recipes) {
[recipeListViewController setRecipe:_recipe];
}

// Reset Selection
[self setSelection:nil];
}

//id RecipeListTableViewController = [segue destinationViewController];
//if ([[segue identifier] isEqualToString:@"showRecipes"]) {
//NSIndexPath *indexPath = [[self tableView] indexPathForSelectedRow];
//id selectedObject = [[self.recipe valueForKey:@"type"] objectAtIndexPath:indexPath];
//[RecipeListTableViewController setRecipe:selectedObject];
//return;

// Obtain Reference to View Controller
//RecipeListTableViewController *recipeListViewController = (RecipeListTableViewController *)segue.destinationViewController;

//Recipe *recipe = nil;
//if ([sender isKindOfClass:[Recipe class]]) {

// the sender is ourselves (user tapped an existing recipe)
//NSIndexPath *indexPath = [self.tableView indexPathForCell:sender];
//recipe = (Recipe *)[self.recipeTypes objectAtIndexPath:indexPath];

//self.recipe.type = [self.managedObjectContext objectAtIndexPath:indexPath];

// Configure View Controller
//[recipeListViewController setManagedObjectContext:self.managedObjectContext];

//NSIndexPath *indexPath = [self.tableView indexPathForCell:sender];

//NSManagedObject *recipe = [[self.recipeType valueForKey:@"type"] objectAtIndexPath:indexPath];

//NSManagedObject *recipe = [self.recipeTypes objectAtIndexPath:indexPath];
//recipeListViewController.recipe = self.recipe;

//recipeListViewController.managedObjectContext = self.managedObjectContext;

}
}


File: RecipeListTableViewController.h

#import <UIKit/UIKit.h>
#import "RecipeAddViewController.h"

@class Recipe, RecipeType;

@interface RecipeListTableViewController : UITableViewController <RecipeAddDelegate, NSFetchedResultsControllerDelegate>

@property (strong, nonatomic) NSManagedObjectContext *managedObjectContext;
@property (nonatomic, strong) RecipeType *recipeType;
@property (nonatomic, strong) Recipe *recipe;


File: RecipeListTableViewController.m

#import "RecipeListTableViewController.h"
#import "RecipeDetailViewController.h"
#import "Recipe.h"
#import "RecipeTableViewCell.h"
#import "Recipe+Extensions.h"
#import "TypeSelectionViewController.h"
#import "IngredientDetailViewController.h"
#import "Ingredient.h"
#import "WhereViewController.h"
#import "FavoriteListTableViewController.h"
#import "RecipeTypeTableViewController.h"
#import "RecipeType.h"


@interface RecipeListTableViewController () <NSFetchedResultsControllerDelegate, UISearchBarDelegate, UISearchResultsUpdating>

@property (nonatomic, strong) NSFetchedResultsController *fetchedResultsController;
@property (nonatomic, strong) NSArray *filteredList;
@property (nonatomic, strong) NSFetchRequest *searchFetchRequest;
@property (nonatomic, strong) UISearchController *searchController;

typedef NS_ENUM(NSInteger, RecipesSearchScope)
{
searchScopeRecipe = 0,
searchScopeIngredients = 1
};


@end

@implementation RecipeListTableViewController

// segue ID when "RecipeCell" is tapped
static NSString *kShowRecipeSegueID = @"showRecipe";

// segue ID when "+" button is tapped
static NSString *kAddRecipeSegueID = @"addRecipe";

#pragma mark === View Life Cycle Management ===
#pragma mark -

- (void)viewDidLoad {

[super viewDidLoad];

// add the table's edit button to the left side of the nav bar
//self.navigationItem.leftBarButtonItem = self.editButtonItem;

//self.navigationController.navigationBar.topItem.title = @"Recipes";
// Set the table view's row height
self.tableView.rowHeight = 124.0;

[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(didChangePreferredContentSize:)
name:UIContentSizeCategoryDidChangeNotification object:nil];
//self.tableView.rowHeight = UITableViewAutomaticDimension;

// No search results controller to display the search results in the current view
self.searchController = [[UISearchController alloc] initWithSearchResultsController:nil];
self.searchController.searchResultsUpdater = self;
self.searchController.dimsBackgroundDuringPresentation = NO;

// Configure the search bar with scope buttons and add it to the table view header
self.searchController.searchBar.scopeButtonTitles = @[NSLocalizedString(@"ScopeButtonRecipe",@"Recipe"),
NSLocalizedString(@"ScopeButtonIngredient",@"Ingredient")];

self.searchController.searchBar.delegate = self;
self.tableView.tableHeaderView = self.searchController.searchBar;
self.definesPresentationContext = YES;
[self.searchController.searchBar setBarTintColor:[UIColor colorWithHue:.42 saturation:0.35 brightness:0.86 alpha:1.0]];
[self.searchController.searchBar setTintColor:[UIColor colorWithHue:.42 saturation:0.65 brightness:0.46 alpha:1.0]];

// The search bar does not seem to set its size automatically
// which causes it to have zero height when there is no scope
// bar. If you remove the scopeButtonTitles above and the
// search bar is no longer visible make sure you force the
// search bar to size itself (make sure you do this after
// you add it to the view hierarchy).
[self.searchController.searchBar sizeToFit];


NSError *error = nil;
if (![[self fetchedResultsController] performFetch:&error]) {
NSAssert(NO, @"Fetch should not fail");
[self showAlert];

NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
//abort();
}
}
- (void)didChangePreferredContentSize:(NSNotification *)notification
{

[self.tableView reloadData];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
self.searchFetchRequest = nil;
}

- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIContentSizeCategoryDidChangeNotification
object:nil];
}

#pragma mark === Accessors ===
#pragma mark -

- (NSFetchRequest *)searchFetchRequest
{
if (_searchFetchRequest != nil)
{
return _searchFetchRequest;
}

_searchFetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Recipe" inManagedObjectContext:self.managedObjectContext];
[_searchFetchRequest setEntity:entity];

NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObjects:sortDescriptor, nil];
[_searchFetchRequest setSortDescriptors:sortDescriptors];

return _searchFetchRequest;
}


#pragma mark - Recipe support

- (void)recipeAddViewController:(RecipeAddViewController *)recipeAddViewController didAddRecipe:(Recipe *)recipe {

if (recipe) {
// show the recipe in the RecipeDetailViewController
[self performSegueWithIdentifier:kShowRecipeSegueID sender:recipe];
}

// dismiss the RecipeAddViewController
[self dismissViewControllerAnimated:YES completion:nil];
}


#pragma mark - UITableViewDataSource Number of Sections

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {

if (self.searchController.active)
{
return 1;
}
else
{
return [[self.fetchedResultsController sections] count];
}
}
#pragma mark - UITableViewDataSource Number of Rows

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (self.searchController.active)
{
return [self.filteredList count];
}
else
{
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section];
return [sectionInfo numberOfObjects];
}
}
#pragma mark - UITableViewDataSource Cell for Row

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

// dequeue a RecipeTableViewCell, then set its recipe to the recipe for the current row
RecipeTableViewCell *recipeCell =
[self.tableView dequeueReusableCellWithIdentifier:@"MyIdentifier" forIndexPath:indexPath];

Recipe *recipe = nil;
if (self.searchController.active)
{
recipe = [self.filteredList objectAtIndex:indexPath.row];
}
else
{
recipe = [self.fetchedResultsController objectAtIndexPath:indexPath];
}
recipeCell.recipe = recipe;
return recipeCell;
}

- (void)configureCell:(RecipeTableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {

Recipe *recipe = (Recipe *)[self.fetchedResultsController objectAtIndexPath:indexPath];
cell.recipe = recipe;
}

#pragma mark - UITableViewDelegate

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {

if ([segue.identifier isEqualToString:kShowRecipeSegueID]) {
// show a recipe
//
RecipeDetailViewController *detailViewController = (RecipeDetailViewController *)segue.destinationViewController;

Recipe *recipe = nil;
if ([sender isKindOfClass:[Recipe class]]) {
// the sender is the actual recipe send from "didAddRecipe" delegate (user created a new recipe)
recipe = (Recipe *)sender;
}
else
{
// the sender is ourselves (user tapped an existing recipe)
NSIndexPath *indexPath = [self.tableView indexPathForCell:sender];
recipe = (Recipe *)[self recipeForIndexPath:indexPath];
//recipe = (Recipe *)[self.fetchedResultsController objectAtIndexPath:indexPath];
}
detailViewController.recipe = recipe;
}
else if ([segue.identifier isEqualToString:kAddRecipeSegueID]) {
// add a recipe
//
Recipe *newRecipe = [NSEntityDescription insertNewObjectForEntityForName:@"Recipe"
inManagedObjectContext:self.managedObjectContext];

UINavigationController *navController = segue.destinationViewController;
RecipeAddViewController *addController = (RecipeAddViewController *)navController.topViewController;
addController.delegate = self; // do didAddRecipe delegate method is called when cancel or save are tapped
addController.recipe = newRecipe;
}
}

// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {

if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the managed object for the given index path
NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
[context deleteObject:[self.fetchedResultsController objectAtIndexPath:indexPath]];

// Save the context.
NSError *error;
if (![context save:&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. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button.
*/
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
}
}


#pragma mark - Fetched results controller

- (NSFetchedResultsController *)fetchedResultsController {

// Set up the fetched results controller if needed.
if (_fetchedResultsController == nil) {
// Create the fetch request for the entity.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Recipe" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];

// Edit the sort key as appropriate.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];

[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:@"Recipe"];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;

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

return _fetchedResultsController;
}

/**

Delegate methods of NSFetchedResultsController to respond to additions, removals and so on.
*/
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {

// The fetch controller is about to start sending change notifications, so prepare the table view for updates.
[self.tableView beginUpdates];
}

- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {

UITableView *tableView = self.tableView;

switch(type) {
case NSFetchedResultsChangeInsert:
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;

case NSFetchedResultsChangeDelete:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;

case NSFetchedResultsChangeUpdate:
[self configureCell:(RecipeTableViewCell *)[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
break;

case NSFetchedResultsChangeMove:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
}
}

- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {

switch(type) {
case NSFetchedResultsChangeInsert:
[self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;

case NSFetchedResultsChangeDelete:
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;

case NSFetchedResultsChangeMove:
NSLog(@"A table item was moved");
break;
case NSFetchedResultsChangeUpdate:
NSLog(@"A table item was updated");
break;

}
}

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {

// The fetch controller has sent all current change notifications,
// so tell the table view to process all updates.
[self.tableView endUpdates];
}

#pragma mark -
#pragma mark === UISearchBarDelegate ===
#pragma mark -

- (void)searchBar:(UISearchBar *)searchBar selectedScopeButtonIndexDidChange:(NSInteger)selectedScope
{
[self updateSearchResultsForSearchController:self.searchController];
}

#pragma mark -
#pragma mark === UISearchResultsUpdating ===
#pragma mark -

- (void)updateSearchResultsForSearchController:(UISearchController *)searchController
{
NSString *searchString = searchController.searchBar.text;
[self searchForText:searchString scope:searchController.searchBar.selectedScopeButtonIndex];
[self.tableView reloadData];
}
- (void)searchForText:(NSString *)searchText scope:(RecipesSearchScope)scopeOption

{
if (self.managedObjectContext)
{
NSString *predicateFormat = @"%K CONTAINS[cd] %@";
NSString *searchAttribute = @"name";

if (scopeOption == searchScopeIngredients)
{
searchAttribute = @"ingredients.name";
}

NSPredicate *predicate = [NSPredicate predicateWithFormat:predicateFormat, searchAttribute, searchText];
[self.searchFetchRequest setPredicate:predicate];

NSError *error = nil;
self.filteredList = [self.managedObjectContext executeFetchRequest:self.searchFetchRequest error:&error];
if (error)
{
NSLog(@"searchFetchRequest failed: %@",[error localizedDescription]);
}
}
}

#pragma mark === Helper methods ===
#pragma mark -

- (Recipe *)recipeForIndexPath:(NSIndexPath *)indexPath {

Recipe *recipe = nil;
if (indexPath) {
if (self.searchController.isActive) {
recipe = [self.filteredList objectAtIndex:indexPath.row];
} else {
recipe = [self.fetchedResultsController objectAtIndexPath:indexPath];
}
}
return recipe;
}
#pragma mark - Show Error Alert
- (void)showAlert {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Could Not Work With Data"
message:@"There was a problem with the data but it is not your fault. If you restart the app, you can try again. Please contact support (redchuck@earthlink.net) to notify us of this issue."
delegate:self
cancelButtonTitle:@"Ok"
otherButtonTitles:@"userInfo", nil];
[alertView show];
}

- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
abort();
}

@end

Answer

First, I think your data model needs a little refinement. Each RecipeType can presumably have many associated Recipes. So the relationship from RecipeType to Recipe should be to-many. If you change this in the data model editor, and regenerate the model classes, you should then have a RecipeType class that looks something like this:

@class Recipe;

@interface RecipeType : NSManagedObject
// not sure what purpose this property serves; it might now be superfluous...
@property (nonatomic, strong) NSManagedObject *type;

@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSSet<Recipe *> *recipes;

@end 

I'll assume for the time being that each Recipe can belong to only one RecipeType. So the inverse relationship, from Recipe to RecipeType should be to-one, and the Recipe class will therefore have a property:

*property (nonatomic, strong) RecipeType *type;

Next, you want your RecipeListTableViewController to display only the Recipes that are related to the relevant RecipeType. To achieve that, you need to add a predicate to the fetched results controller. In the fetchedResultsController method, add:

if (self.recipeType) {
    fetchRequest.predicate = [NSPredicate predicateWithFormat:@"type == %@", self.recipeType];
}

(You will also need to amend your search to likewise limit the search to the relevant RecipeType). In the RecipeTypeTableViewContoller, your prepareForSegue needs only to pass the correct RecipeType:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {    
    if ([[segue identifier] isEqualToString:@"showRecipes"]) {
        NSLog(@"Setting RecipeType for the RecipeListTableViewController");
        NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
        RecipeType *selectedType = [self.fetchedResultsController objectAtIndexPath:indexPath];
        RecipeListTableViewController *recipeListViewController = segue.destinationViewController;
        recipeListViewController.recipeType = selectedType;
        recipeListViewController.managedObjectContext = self.managedObjectContext;
    }
}

Whenever you add a new Recipe, you will need to assign it to the correct RecipeType. So in the RecipeListViewController amend your prepareForSegue to set the relationship:

...
else if ([segue.identifier isEqualToString:kAddRecipeSegueID]) {
    // add a recipe
    //
    Recipe *newRecipe = [NSEntityDescription insertNewObjectForEntityForName:@"Recipe" inManagedObjectContext:self.managedObjectContext];
    newRecipe.type = self.recipeType;

    UINavigationController *navController = segue.destinationViewController;
    RecipeAddViewController *addController = (RecipeAddViewController *)navController.topViewController;
    addController.delegate = self;  // do didAddRecipe delegate method is called when cancel or save are tapped
    addController.recipe = newRecipe;
}
Comments