Search code examples
objective-ccocoacore-datanspredicatensarraycontroller

How to use predicate on arrayController and Core Data


I have a NSOutlineView that shows data from my Core Data store. The data is presented using an NSarrayController linked to my managedObjectContext and displayed in the NSOutlineView using NSTreeController (much like described here). I would like to filter what data are shown using NSPredicate (or something else) but I can't get this to work. Note that this is OSX and not iOS so I can't use NSFetchedResultsController.

I am able to retrieve the correct data and store these in a NSArray. However, enabling an automatic update of what I see in my outlineview using only the filtered data does not work. This is what I currently have:

[arrayController setManagedObjectContext:_coreDataHelper.context];
NSFetchRequest *request = [[NSFetchRequest alloc] init];

NSEntityDescription *entity = [NSEntityDescription entityForName:@"SDRDFileObject"
                                          inManagedObjectContext:_coreDataHelper.context];

[request setEntity:entity];

NSNumber *directionLimit = @1;
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"direction = %@", directionLimit];
[request setPredicate:predicate];

NSError *error;
[arrayController setContent:[_coreDataHelper.context executeFetchRequest:request error:&error]];

[_coreDataHelper.context reset];
[arrayController fetch:self];
[outlineView reloadData];

As you can see my question is close to this one, but I am still struggling. If I do get this to work using setContent for the arrayController I also assume that I will run into trouble with having mismatch between the Core Data context and the content of the arrayController. Suggestions or an example for how to correctly do this is greatly appreciated.


Solution

  • Suggestion 1: Try ditching the array controller.

    I'm not entirely sure what the objective of the author of the first link that you direct us to is, but he/she does something that, at least in the most common NSOutlineView setup, is a little odd. The only controller object required to extract values from Core Data and display them in an outline view is an NSTreeController. The tutorial on the other hand suggests interposing an array controller: "In your NIB file you’ll need an array controller bound to your NSManagedObjectContext, set to Entity mode, and with a Fetch Predicate of parent == nil. This will fetch our top level objects – entities without a parent." My opinion is that this is what you should be doing with your tree controller not an array controller. With thin in mind I think the first thing you should do is get you're data appearing correctly with just a tree controller, once you've achieved this, you can start to think about filtering the outline view.

    Suggestion 2: Filtering

    Unlike NSArrayController, NSTreeController objects have no filterPredicate in their class reference. I'm not sure why this is, but with an outline view you have to implement filtering behaviour yourself. This can be a bit tricky, but it's not too difficult. For instance, in one of my projects, I have a large drag-and-drop compatible tree controller/outline view setup which apes the way Xcode filters files when you type something into the search bar positioned in the bottom left-hand corner. I did this by adding a category to NSTreeController called -filterContentUsingPredicate with returns an array. Once the filtering is complete, I use a call to [-NSTreeController setContent:] to update the contents of the outline view to reflect the filter.