NSFetchedResultsController setLimit for each Section - iphone

How would you go about setting the limit of each section in a tableview controlled by a NSFetchedResultsController? For example: I have a bunch of shows that each have start dates and play on different stages. I want to be able to show only who's up next on each stage. So I need not a limit on the whole request, but a limit on each section.
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Event"];
NSMutableArray *predicates = [NSMutableArray array];
[predicates addObject:[NSPredicate predicateWithFormat:#"startDate > %#", [NSDate date]]];
request.predicate = [NSCompoundPredicate andPredicateWithSubpredicates:predicates];
NSSortDescriptor *stageSort = [[NSSortDescriptor alloc] initWithKey:#"stage.id" ascending:YES];
NSSortDescriptor *dateSort = [[NSSortDescriptor alloc] initWithKey:#"startDate" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:stageSort, dateSort, nil];
[request setSortDescriptors:sortDescriptors];
self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:#"stage.id"
cacheName:nil];
This kind of works if I limit the it in the tableview delegate methods but often crashes when the fetch changes and it trys to update the table. I think it's because there are a lot more objects in the NSFetchedResultsController than are showing and the animation of changing these breaks it.
How would I go about setting a more effective limit per section? I only want the first result per section.

In general, I think your approach is feasible.
Just fix your table update routines. Before inserting a row, check your logic if it should be done, and if not, skip the insert.

Related

issue with sorting data in UITableview

I have implement a UItable with data sourced via Core Data. The table works fine and presents the data correctly, drills down.. etc.
However, it has the following problem: it presents the content data in a different order every time. I would like it at least to appear consistently or even better alphabetically.
Any ideas on why this might be happening or a specific property or method I should be reviewing in the docuemntation?
Help much appreciated
You need to set an NSSortDescriptor on your NSFetchRequest
NSSortDescriptor *sortDescriptorName = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES selector:#selector(caseInsensitiveCompare:)];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sortDescriptorName]];
Then create your NSFetchedResultsController with the fetchRequest object.
This would cause your list to appear sorted alphabetically using the property "name" and ignore case while sorting.
You can simply add a sort descriptor to the request. In this example, the data object has a numeric column for "sortOrder", but you could sort on most anything.
- (NSMutableArray *)loadData {
NSManagedObjectContext *context = [self managedObjectContext];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"NameOfEntity" inManagedObjectContext:context];
[request setEntity:entity];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"sortOrder" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[request setSortDescriptors:sortDescriptors];
[sortDescriptors release];
[sortDescriptor release];
NSError *error = nil;
NSMutableArray *mutableFetchResults = [[context executeFetchRequest:request error:&error] mutableCopy];
if (mutableFetchResults == nil) {
// Handle the error.
}
[request release];
return [mutableFetchResults autorelease];
}
I have not done much with CoreData other than to read about it and review some examples, but I would say this:
It sounds like your data is simply coming out of CoreData unsorted, and the order is in fact not guaranteed UNLESS you provide some additional hint about how you would like it sorted, with say a sort descriptor or something.
Sorry I can't provide a concrete reference, but there has to be a way to both fetch data and specify the order in which it is returned.
Alternatively, you could (behind the scenes) fetch all the table data from CoreData and then sort it yourself, but I think that defeats the purpose of using CoreData in the first place and discards a lot of the functionality of CoreData that is likely more efficient than anything you could write yourself to massage the data.

Core Data Relationships, NSPredicates and the NSFetchedResultsController

This has been driving me nuts all day.
I have a weird bug that I think I have narrowed down to an NSPredicate. I have two entities: List and Person. List has a to-many relationship to Person called persons and Person has a to-many relationship to List called lists.
I pass to my a tableview controller a List object. I then want that tableview controller to display the Persons that belong to that list object. I am doing this with a NSFetchedResultsController.
When setting up the NSFRC, I have the following code (memory management omitted for clarity). The List in question is myList:
// Create the request and set it's entity
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Person" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
// Create a predicate to get the persons that belong to this list
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(ANY lists == %#)", myList];
// Assign this predicate to the fetch request
[fetchRequest setPredicate:predicate];
// Define some descriptors
NSSortDescriptor *locationDescriptor = [[NSSortDescriptor alloc] initWithKey:#"location" ascending:YES];
NSSortDescriptor *lastNameDescriptor = [[NSSortDescriptor alloc] initWithKey:#"lastName" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:locationDescriptor, lastNameDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
// Create and initialize the fetch results controller.
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:#"location" cacheName:nil];
self.fetchedResultsController = aFetchedResultsController;
fetchedResultsController.delegate = self;
I think the problem is with this line (because it disappears if I remove it):
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(ANY lists == %#)", myList];
What is happening is when the parent view passes myList to the tableview controller, the simulator just hangs. No crash log in the console or anything. It's almost as if it's just taking AGES to sort out the NSFRC.
Is this a problem with the predicate I'm using?
Do you you need to use NSFetchedResultsController when you can obtain the Persons from the list passed into the tableViewController?
NSSet *people = myList.persons;
You are correct, you can just use myList.persons, an NSFetchedResultsController is not necessary in this situation.
Thanks for the suggestions re: using an NSSet. After hours of bug-tracking I realised that the problem lie in my cellForIndexPath method of the table view (so, unrelated to the NSFRC).

Dealloc'd Predicate crashing iPhone App!

To preface, this is a follow up to an inquiry made a few days ago:
https://stackoverflow.com/questions/2981803/iphone-app-crashes-when-merging-managed-object-contexts
Short Version: EXC_BAD_ACCESS is crashing my app, and zombie-mode revealed the culprit to be my predicate embedded within the fetch request embedded in my Fetched Results Controller. How does an object within an object get released without an explicit command to do so?
Long Version:
Application Structure
Platforms View Controller -> Games View Controller (Predicated upon platform selection) -> Add Game View Controller
When a row gets clicked on the Platforms view, it sets an instance variable in Games View for that platform, then the Games Fetched Results Controller builds a fetch request in the normal way:
- (NSFetchedResultsController *)fetchedResultsController{
if (fetchedResultsController != nil) {
return fetchedResultsController;
}
//build the fetch request for Games
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"Game"
inManagedObjectContext:context];
[request setEntity:entity];
//predicate
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"platform == %#",
selectedPlatform];
[request setPredicate:predicate];
//sort based on name
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"name"
ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[request setSortDescriptors:sortDescriptors];
//fetch and build fetched results controller
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc]
initWithFetchRequest:request
managedObjectContext:context
sectionNameKeyPath:nil
cacheName:#"Root"];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
[sortDescriptor release];
[sortDescriptors release];
[predicate release];
[request release];
[aFetchedResultsController release];
return fetchedResultsController;
}
At the end of this method, the fetchedResultsController's _fetch_request -> _predicate member is set to an NSComparisonPredicate object. All is well in the world.
By the time - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section gets called, the _predicate is now a Zombie, which will eventually crash the application when the table attempts to update itself.
I'm more or less flummoxed. I'm not releasing the fetched results controller or any of it's parts, and the only part getting dealloc'd is the predicate. Any ideas?
EDIT:
As a test, I added this line to the Fetched Results Controller method:
[fetchedResultsController.fetchRequest.predicate retain];
And now it doesn't crash, but that seems like a patch, not something I should be doing.
You shouldn't be releasing your predicate variable. You didn't invoke new, alloc, retain, or copy (This is the "narc" rule) to create the predicate, so you are not responsible for releasing it. That's where your zombie is coming from.

How do you sort NSNumber with NSSortDescriptor?

I'm having trouble sorting an NSNumber using NSSortDescriptor. For whatever reason it won't sort into the correct order. What am I doing wrong?
- (NSNumber *)sortNumber {
NSNumber *sum = [NSNumber numberWithFloat:([self.capacity floatValue] / [self.availability floatValue])];
return sum;
}
It is defined as an Int32 in the xcdataModel and called by this class to sort.
- (NSFetchedResultsController *)fetchedResultsController {
if (fetchedResultsController == nil) {
NSFetchRequest *fetchRequest = [[[NSFetchRequest alloc] init] autorelease];
[fetchRequest setReturnsObjectsAsFaults:NO];
[fetchRequest setEntity:[NSEntityDescription entityForName:#"Event" inManagedObjectContext:managedObjectContext]];
NSArray *sortDescriptors = nil;
} if ([fetchSectioningControl selectedSegmentIndex] == 0) {
sortDescriptors = [NSArray arrayWithObject:[[[NSSortDescriptor alloc] initWithKey:#"sortNumber" ascending:YES selector:#selector(compare:)] autorelease]];
}
[fetchRequest setSortDescriptors:sortDescriptors];
EDIT:Yeah, in the cold light of day this needs a little more explanation. I'll try to explain the project. This is a CoreData app, the values 'capacity' and 'availability' are derived from parsing an XML file with SAX and attaching them to the Managed Object Model where they are defined as Strings, originally they would have been numeric in the XML.
These are then defined in a Class where the calculation above has been made and attached to the same Object Model (if the code above is right then perhaps this is where the problem is?). All this has been in effort to obtain a percentage value that I would like to use to sort a TableView. (BTW, I realise they need swapping around, oops) The second bit of code is where this happens using NSFetchResultsController and ManagedObjectContext. I know this bit works because I'm also sorting this list by other attributes set to if selectedSegmentIndex == 0 etc. They all sort correctly.
I hope this makes a bit more sense.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"sortNumber" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[request setSortDescriptors:sortDescriptors];
you can use NSSortDescriptor without calling compare method. It will sort itself in you desired order.

Group of sections not consistent when using NSFetchedResultsController

I am working with a NSFetchedResultsController whose fetchRequest has a predicate. However, it seems that the query doesn't give me consistent groupings each time I execute it.
I've set the 'sectionNameKeyPath' for the NSFetchedResultsController and I get a different number of sections returned based on whether I have been working with the root object immediately prior to running the fetch. Sometimes I get 3 sections and other times, it returns 1 section, the expected result.
How I am creating the FetchRequestController:
// Create the fetch request for the entity.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Configure the request's entity and its predicate.
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Employee"
inManagedObjectContext:context];
[fetchRequest setEntity:entity];
// The predicate to find all employees associated with a Group
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"ANY SELF.groups IN %#",
[division groups]];
[fetchRequest setPredicate:predicate];
// Sort based on create date and time
NSSortDescriptor *createDateSortDcptor = [[NSSortDescriptor alloc] initWithKey:#"createDateTime" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:createDateSortDcptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
// should be grouped by the 'Group' employee belongs to.
NSFetchedResultsController *controller =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:context
sectionNameKeyPath:#"groups"
cacheName:#"Root"];
My object model is the same that was outlined in this other question:
https://stackoverflow.com/questions/1580236/how-to-setup-a-predicate-for-this-query
Is there a way to make sure I am getting consistent grouping each time?
It turns out that it's simple as doing:
NSFetchedResultsController *controller =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:context
sectionNameKeyPath:#"groups.name"
cacheName:#"Root"];
I didn't realize I could append 2nd level property names within the 'sectionNameKeyPath'