How to sort UITableView content with two dataSources - iphone

Hi I have following issue:
I'm programming a game and everytime the player finishes a score is submitted. So i got two Datasources: Score and Mode. And i want to sort these Highscores in 4 sections, the 4 modes i have. And in these sections it should be sorted by Score (highest on the top).
Yet I got this code:
- (NSFetchedResultsController *)fetchedResultsController{
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Event" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
// Set the batch size to a suitable number.
[fetchRequest setFetchBatchSize:20];
// Edit the sort key as appropriate.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"score" ascending:NO];
NSArray *sortDescriptors = #[sortDescriptor];
[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:#"Master"];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
_fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:#"mode" cacheName:nil];
_fetchedResultsController.delegate = self;
[_fetchedResultsController performFetch:nil];
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error]) {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return _fetchedResultsController;}
But all I get is a pretty random Sorting. The first time it worked but after a few games it messed up!
My modes are: 10,20,30,60 (seconds) The golden badge on the left shows what section it should be sorted in.
Hope someone can help me.
iPhone Screenshot

You need to add another sort descriptor for mode as well as for score.
You need to sort first by mode and then by score. As you are currently sorting by score and sectioning by name you'll get...
Mode 1:
- Score 100
- Score 99
Mode 2:
- Score 80
Mode 1:
- Score 70
etc...
Change your sort descriptor code like this...
NSSortDescriptor *modeSD = [[NSSortDescriptor alloc] initWithKey:#"mode" ascending:YES];
NSSortDescriptor *scoreSD = [[NSSortDescriptor alloc] initWithKey:#"score" ascending:NO];
NSArray *sortDescriptors = #[modeSD, scoreSD];
[fetchRequest setSortDescriptors:sortDescriptors];
That should sort it for you (pun very much intended ;-) )
If you would like four separate sections then your NSFRC will look like this...
// note the section name key path
NSFetchedResultsController *nsfrc = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:self.managedObjectContext sectionNameKeyPath:#"mode" cacheName:nil];
This will then split the data into sections and rows.
Count of sections is [[nsfrc allSections] count];
And so on...

Related

Core Data to-many relationship get data

In my code i want to create tableView with List sections. I use scheme like this one:
I use NSFetchResultController which i define in this way:
- (NSFetchedResultsController *)fetchedResultsController {
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"Item" inManagedObjectContext:coreDataController.masterManagedObjectContext];
[fetchRequest setEntity:entity];
NSSortDescriptor *sort = [[NSSortDescriptor alloc]
initWithKey:#"addedAt" ascending:YES];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]];
[fetchRequest setFetchBatchSize:20];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"itemIsChecked = 1"];
[fetchRequest setPredicate:predicate];
[fetchRequest setResultType:NSDictionaryResultType];
NSFetchedResultsController *theFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:coreDataController.masterManagedObjectContext sectionNameKeyPath:#"toList.listName"
cacheName:nil];
self.fetchedResultsController = theFetchedResultsController;
_fetchedResultsController.delegate = self;
return _fetchedResultsController;
}
Now in cellForRowAtIndexPath: i want to get data form my fetchResultController, so i do this in way:
Item *item = [self.fetchedResultsController objectAtIndexPath:indexPath];
and then if i want to access one of the item's field (for example itemText), it crash:
NSLog(#"item itemtext = %#", item.itemText);
with error:
-[NSKnownKeysDictionary1 itemText]: unrecognized selector sent to instance 0x1215fd90
What i do wrong in my code?
You have set
[fetchRequest setResultType:NSDictionaryResultType];
and therefore the fetched results controller returns NSDictionary objects, not Item objects. So your element
Item *item = [self.fetchedResultsController objectAtIndexPath:indexPath];
is a NSDictionary, not an Item. Since dictionaries do not have a itemText method, item.itemText crashes. You could retrieve the value from the dictionary with
NSDictionary *item = [self.fetchedResultsController objectAtIndexPath:indexPath];
NSLog(#"item itemtext = %#", [item objectForKey:#"itemText"]);
But if you don't have a specific reason to set the result type to NSDictionaryResultType, you should just delete that line. Change tracking of the fetched results controller (i.e. automatic table view updates) do not work with resultType == NSDictionaryResultType.
Note also that if you have set a sectionNameKeyPath, then you must add a sort descriptor with the same key path "toList.listName" and use it as first sort descriptor for the fetch request.
unrecognized selector sent to instance generally occurs due to bad memory management. Check if you are trying to point an object which was released earlier. Also check for IBOutlet connection in xib for lable itemText.

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

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.

Accesing sub entity reletionship/property in NSFetchedResultsController

I'm trying to acces a property from a managedobject relationship in one of my entity but i keep getting a "nil" object from it. Here's a summary of the entities and there relation.
Entity: House
Relationships: adress (1 to 1)
Entity: Adresse
Properties: street (NSString)
Relationships: house (1 to 1)
So basically a "House" can only have one "Adresse". I want to be able to sort by the street property from the adress relationship when i fetch a House. Here's the code that i'm using but can't seem to make it work out. What am i missing?
- (NSFetchedResultsController *)fetchedResultsController {
// Set up the fetched results controller if needed.
if (fetchedResultsController == nil) {
MyAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *managedObjectContext = [appDelegate managedObjectContext];
// Create the fetch request for the entity.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:#"House" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
// Edit the sort key as appropriate.
NSString *sectionKey = #"adresse.rue";
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:sectionKey ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
[fetchRequest setFetchBatchSize:20];
// 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:sectionKey cacheName:#"House"];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
[aFetchedResultsController release];
[fetchRequest release];
[sortDescriptor release];
[sortDescriptors release];
}
return fetchedResultsController;
}
Your code looks OK. Your fetch request is valid. If it's not throwing any exceptions, then your key path is specified properly.
I'm not quite sure what you mean by "getting a nil object from it". If you mean your fetched results controller doesn't contain any objects, make sure that you're calling performFetch: before you try accessing any objects. The controller will be empty until you do this.
Does this code work when you leave out the sort descriptors and key path?

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'