views:

772

answers:

3

In my iPhone Core Data app I have it configured in a master-detail view setup.

The master view is a UITableView that lists objects of the List entity. The List entity has a to-many relationship with the Task entity (called "tasks"), and the Task entity has an inverse to-one relationship with List called "list".

When a List object is selected in the master view, I want the detail view (another UITableView) to list the Task objects that correspond to that List object. What I've done so far is this:

In the detail view controller I've declared a property for a List object:

@property (nonatomic, retain) List *list;

Then in the master view controller I use this table view delegate method to set the list property of the detail view controller when a list is selected:

- (void)tableView:(UITableView *)aTableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    NSManagedObject *selectedObject = [[self fetchedResultsController] objectAtIndexPath:indexPath];
    detailViewController.list = (List*)selectedObject; 
}

Then, I've overriden the setter for the list property in the detail view controller like this:

- (void)setList:(List*)newList
{
    if (list != newList) {
        [list release];
        list = [newList retain];

        NSPredicate *newPredicate = [NSPredicate predicateWithFormat:@"(list == %@)", list];
        [NSFetchedResultsController deleteCacheWithName:@"Root"];
        [[[self fetchedResultsController] fetchRequest] setPredicate:newPredicate];

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

What I'm doing here is setting a predicate on the fetched results to filter out the objects so that I only get the ones that belong to the selected List object. The fetchedResultsController getter for the detail view controller looks like this:

- (NSFetchedResultsController *)fetchedResultsController {
    if (fetchedResultsController == nil) {
        NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
        NSEntityDescription *entity = [NSEntityDescription entityForName:@"Task" inManagedObjectContext:managedObjectContext];
        [fetchRequest setEntity:entity];

    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"FALSEPREDICATE"];
    [fetchRequest setPredicate:predicate];

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

        [fetchRequest setSortDescriptors:sortDescriptors];

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

        [aFetchedResultsController release];
        [fetchRequest release];
        [sortDescriptor release];
        [sortDescriptors release];
    }

    return fetchedResultsController;
}    

Its almost unchanged from the default in the Core Data project template, the change I made is to add a predicate that always returns false, the reason being that when there is no List selected I don't want any items to be displayed in the detail view (if a list is selected the predicate is changed in the setter for the list property).

However, when I select a list item, nothing really happens. Nothing in the table view changes, it stays empty. I'm sure my logic is flawed in several places, advice is appreciated

Thanks

A: 

Maybe I don't understand your setup completely, but

detailViewController.list.task

(or whatever that relationship is called) will return an NSSet of any Task objects that are associated with the List object.

blindJesse
+1  A: 

You don't need to set up a NSFetchedResultsController for your task list. Since you are passing in the List entity, you can just ask the List entity for the tasks:

NSSet *tasks = [[self list] valueForKey:@"tasks"];
NSSortDescriptor *sort = ...;
NSArray *sorted = [[tasks allObjects] sortedArrayUsingDescriptors:[NSArray arrayWithObject:sort]];
[self setTasks:sorted];

Then have the UITableviewDatasource methods run off of that tasks array.

Marcus S. Zarra
That looks much easier, will give it a try, thanks!
macatomy
A: 

Well, I have a similar issue, and I posted my question at http://stackoverflow.com/questions/3494095/how-to-switch-uitableviews-nsfetchedresultscontroller-or-its-predicate-program.

After I posted it, I decided to try out your code. And you know what? It works, with only a small addition:

    [self.tableView reloadData];

Perhaps you can go back to your initial code and confirm.

Regards.

Jean-Denis Muys