Group of sections not consistent when using NSFetchedResultsController - iphone

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'

Related

Dynamically break UITableView into arbitrary sections?

I have a core data application which displays a list of custom objects, each representing theatrical productions.
Each show object has a bunch of properties - name, logo, opening date, show type.
I can retrieve them fine, and sort by type just fine - but I would like each show type to be its own section in the table.
I don't know ahead of time how many show types will be represented in the result set, so I don't know initially what the numberofsections should be.
So I guess the question is - how would I go about dividing the result set into sections grouped by type?
Currently I'm doing this:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Show" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
[fetchRequest setFetchBatchSize:20];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"type" ascending:NO];
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:#"Root"];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
I would write numberOfSections method so that it dynamically checks for the number of types. Then, whenever a new type rolls in, just do a [tableView reloadData];

Some sorting problems (Core Data beginner)

Maybe there is a simple solution to this, but I'm getting headache of this, I'm fairly new with all this Core Data stuff:
I have a BankAccount class/entity with an "index" attribute, used for sorting, and a "transactions" to-many relationship to the Transaction class/entity. This Transaction entity has a "date" attribute, that I want to use for sorting too.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:[NSEntityDescription entityForName:#"BankAccount" inManagedObjectContext:self.managedObjectContext]];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"index" ascending:YES];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sortDescriptor]];
This works well and delivers me the BankAccount objects nicely sorted by "index". But, each BankAccount object contains a NSSet "transactions" that is, ofcourse, not sorted at all. How can I get these transactions sorted by the "date" attribute, and is this possible within the same fetch request?
Many thanks in advance.
To do this you would need to fetch the Transactions ordered.
What you would need is:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:[NSEntityDescription entityForName:#"Transaction"
inManagedObjectContext:self.managedObjectContext]];
[fetchRequest setPropertiesToFetch :[NSArray arrayWithObjects:#"date", #"bankAccount", nil]];
[fetchRequest setSortDescriptors:[NSArray arrayWithObjects:
[[[NSSortDescriptor alloc] initWithKey:#"date"
ascending:YES
selector:#selector(compare:)] autorelease],
[[[NSSortDescriptor alloc] initWithKey:#"bankAccount.index"
ascending:YES] autorelease],
nil]];
I'm assuming that Transaction is the name of the entity for transactions, and that bankAccount is the relationship from entity Transaction to BankAccount.

Sort on to-many relationship using a NSFetchedResultsController

I'm trying to use the NSFetchedResultsController in my app, but have a problem to sort my data. I get the following error when trying to sort the result using a relationship that is two levels down from the entity:
* Terminating app due to uncaught exception
'NSInvalidArgumentException', reason:
'to-many key not allowed here'
My data model is set up this way:
Item <<---> Category <--->> SortOrder
<<---> Store
In other words: Each item belongs to one category. Categories can have different sort orders for each store that includes a certain category.
So, I'm creating a fetch request to find all items for a certain store and would like to present the result using category names as sections, and sorted on the sort order.
When I perform the the fetch (last line below), I get the above error.
NSManagedObjectContext* context = [appDelegate managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(status != %d) AND (ANY category.sort.include == YES) AND (ANY category.sort.store == %#)", ItemStatusDefault, store];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Item" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
[fetchRequest setPredicate:predicate];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"category.sort.order" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
[sortDescriptors release];
[sortDescriptor release];
self.resultsController = [[NSFetchedResultsController alloc]
initWithFetchRequest:fetchRequest
managedObjectContext:context
sectionNameKeyPath:#"category.name"
cacheName:nil];
[fetchRequest release];
NSError *error;
BOOL success = [self.resultsController performFetch:&error];
If I change the sorting to, say, category names, it works.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"category.name" ascending:YES];
How can I get the NSSortDescriptor to sort on the sort order?
UPDATE:
So it seems this is not possible. I got a suggestion to create a transient property and sort on that, but Apple documentation clearly states
You cannot fetch using a predicate
based on transient properties
My conclusion here is that I cannot use NSFetchedResultsController out of the box. I need to either access the array of objects the NSFetchResultsController gives me and sort in memory, or setup my own fetch requests and skip NSFetchedResultsController.
iOS 5 provide now ordered relationships
https://developer.apple.com/LIBRARY/ios/releasenotes/DataManagement/RN-CoreData/index.html
UPDATE:
Link updated
Reference : "Core Data Release Notes for OS X v10.7 and iOS 5.0"

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).

Calling ViewController returns nil

I am trying to resolve this for days at this stage and I'm hoping you can help.
I have two ViewControllers which query two different tables from the same database using Core Data. The first ViewController is opened with the app and displays fine. The second is called from within the first ViewController, using a pretty standard fetch setup:
- (NSFetchedResultsController *)fetchedClients {
// Set up the fetched results controller if needed.
if (fetchedClients == nil) {
// Create the fetch request for the entity.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Clients"
inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
// Edit the sort key as appropriate.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"clientsName" 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:managedObjectContext
sectionNameKeyPath:nil
cacheName:#"Root"];
aFetchedResultsController.delegate = self;
self.fetchedClients = aFetchedResultsController;
[aFetchedResultsController release];
[fetchRequest release];
[sortDescriptor release];
[sortDescriptors release];
}
return fetchedClients;
}
When I call [self.fetchedClients sections], I get a nil (0x0) return.
I have examined the database using an external application to ensure data exists in the "Clients" table.
Are you compiling against 3.0? Thee is a bug in 3.0 that was fixed in 3.1 that can cause this issue.
update
Are you calling -performFetch: on the returned NSFetchedResultsController? I dont't see it in the code you posted.