Some sorting problems (Core Data beginner) - iphone

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.

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];

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.

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

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'