I'm having a problem I don't know where it comes from, related to CoreData. In my database, a have a set of categories (with name and description), which contain elements (using a one-to-many relationship).
I want to divide my table view in sections given an attribute of the Category class, but when I try to do it using sectionNameKeyPath:, the resulting NSFetchedResultsController has 0 sections. If I pass nil to this parameter, it has 1 section.
The code is the following:
- (NSFetchedResultsController*) fetchedResultsController
{
if(fetchedResultsController)
return fetchedResultsController;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Category"
inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
// Set the batch size to a suitable number.
[fetchRequest setFetchBatchSize:10];
// Edit the sort key as appropriate.
NSSortDescriptor *checkDescriptor = [[NSSortDescriptor alloc] initWithKey:#"checked"
ascending:YES];
NSSortDescriptor *indexDescriptor = [[NSSortDescriptor alloc] initWithKey:#"orderIndex"
ascending:YES];
NSArray *sortDescriptors = #[checkDescriptor, indexDescriptor];
[fetchRequest setSortDescriptors:sortDescriptors];
// Edit the section name key path and cache name if appropriate.
// nil for section name key path means "no sections".
fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:#"checked"
cacheName:nil];
NSError *error = nil;
if (![fetchedResultsController performFetch:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
return nil;
} else {
fetchedResultsController.delegate = self;
return fetchedResultsController;
}
}
See the NSFetchedResultsController documentation: The key used for sectionNameKeyPath ("name" in your case) must be the same key used in the first sort descriptor ("checked" in your case). They can be different, but then both keys must generate the same relative ordering.
In your case I assume that you want to add an additional sort descriptor on "name" and use that as the first sort descriptor.
Related
I'm trying to sort by date then start time. Start time is minutes from midnight. So if the start time is < 100 it will not sort properly.
- (NSFetchedResultsController *)fetchedResultsController {
if (fetchedResultsController != nil) {
return fetchedResultsController;
}
/*
Set up the fetched results controller.
*/
// Create the fetch request for the entity.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Appointments" inManagedObjectContext:[[DataManager sharedInstance] managedObjectContext]];
[fetchRequest setEntity:entity];
[fetchRequest setIncludesPendingChanges:YES];
// Set the batch size to a suitable number.
//[fetchRequest setFetchBatchSize:20];
// Sort using the date / then time property.
NSSortDescriptor *sortDescriptorDate = [[NSSortDescriptor alloc] initWithKey:#"date" ascending:YES];
NSSortDescriptor *sortDescriptorTime = [[NSSortDescriptor alloc] initWithKey:#"start_time" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptorDate, sortDescriptorTime, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
// Use the sectionIdentifier property to group into sections.
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:[[DataManager sharedInstance] managedObjectContext] sectionNameKeyPath:#"date" cacheName:#"List"];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
NSLog(#"FetchedController: %#", fetchedResultsController);
return fetchedResultsController;
}
How could I make this sort integers properly?
If start_time is a string then it will be sorted alphabetically which means that aa is before b which also means that 11 is before 2.
To sort in a more human friendly way use NSString's localizedStandardCompare: as a selector.
[NSSortDescriptor sortDescriptorWithKey:#"start_time" ascending:YES selector:#selector(localizedStandardCompare:)]
I am populating a UITableView from Core Data using an NSFetchedResultsController. The data is coming from an NSManagedObject subclass generated by Mogenerator called MenuItem. The MenuItem entity has a SectionID parameter that is an NSNumber and this is used to determine what section the item should be in within the table view.
I performed a test fetch on the data and confirmed that Core Data was populated correctly. All was fine.
The NSFetchedResultsController is created as follows and the sectionKeyNamePath is set to #"sectionId":
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"MenuItem" inManagedObjectContext:self.persistenceController.moc];
[fetchRequest setEntity:entity];
fetchRequest.sortDescriptors = #[[NSSortDescriptor sortDescriptorWithKey:#"sectionId" ascending:YES], [NSSortDescriptor sortDescriptorWithKey:#"rowId" ascending:YES]];
NSFetchedResultsController *frc = nil;
frc = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:self.persistenceController.moc
sectionNameKeyPath:#"sectionId"
cacheName:nil];
The problem (and solution):
With this code, the sections are not identified. The NSFetchedResultsController always returns 0. This worked previously when I created the NSMO subclass manually, so I figured it was something related to Mogenerator.
If I change the sectionNameKeyPath to be #"primitiveSectionId" then it works.
Hopefully this will help somebody in the future, but I don't understand what is going on here. Please could somebody explain why this fixes the problem?
Thanks
Why are you setting the FRC to nil? Try this (it works for me using an NSNumber Attribute):
With mogenerator you can get the entity using "[MenuItem entityName]" and access attributes using MenuItemAttributes.sectionId, for example.
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:[MenuItem entityName]];
fetchRequest.sortDescriptors = #[[NSSortDescriptor sortDescriptorWithKey:MenuItemAttributes.sectionId ascending:YES], [NSSortDescriptor sortDescriptorWithKey:MenuItemAttributes.rowID ascending:YES]];
NSFetchedResultsController *frc = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:self.persistenceController.moc
sectionNameKeyPath:MenuItemAttributes.sectionId
cacheName:nil];
// Make sure you set your frc as the delegate
frc.delegate = self;
NSError *error = nil;
if (![fetcher performFetch:&error]) {
/*
* Did you execute the request?
*/
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
NSLog(#"%lu", [frc.sections count]);
I am having a Core Data problem with NSFetchedResultsController. I have a one to many relationship between a parent and child entity. The array in the childFetchedResults.fetchedObjects property is NOT sorted by number (number is an int32 property in the model). It doesn't seem to matter if I use the MagicalRecord convenience category methods or not.
NSFetchRequest *req = [Child MR_requestAllSortedBy:#"number" ascending:YES withPredicate:[NSPredicate predicateWithFormat:#"parent = %#", self.parent]];
childFetchedResults = [[NSFetchedResultsController alloc] initWithFetchRequest:req managedObjectContext:[NSManagedObjectContext MR_defaultContext] sectionNameKeyPath:nil cacheName:nil];
childFetchedResults.delegate = self;
NSError *error;
[childFetchedResults performFetch:&error];
NSLog(#"fetched objects: %#", childFetchedResults.fetchedObjects);
However, if I sort the array of fetched objects using the exact same sort descriptor, it works fine:
NSLog(#"fetched objects: %#", [childFetchedResults.fetchedObjects sortedArrayUsingDescriptors:[NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"number" ascending:YES]]]);
I gather you can only use comparators which Core Data can pass on to the SQLite store when specifying sort descriptors for a fetch request. But I feel like that shouldn't matter in this case since sorting by a number has got to be supported by SQLite.
Anyone solved this? I feel like it's a similar issue to the one described here: NSSortDescriptor not being called.
As for MR_requestAllSortedBy, it's in MagicalRecord, here is the implementation:
+ (NSFetchRequest *) MR_requestAllSortedBy:(NSString *)sortTerm ascending:(BOOL)ascending inContext:(NSManagedObjectContext *)context
{
NSFetchRequest *request = [self MR_requestAllInContext:context];
NSSortDescriptor *sortBy = [[NSSortDescriptor alloc] initWithKey:sortTerm ascending:ascending];
[request setSortDescriptors:[NSArray arrayWithObject:sortBy]];
return request;
}
So this was caused by fetching against a nested MOC with unsaved changes. Either fetching with the parent MOC or saving the nested MOC prior to executing the fetch resolves the problem. Similar to what was going on in this question: NSSortdescriptor ineffective on fetch result from NSManagedContext
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc]init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"RemainderDataBase" inManagedObjectContext:[self managedObjectContext]];
NSSortDescriptor *nameSort = [[NSSortDescriptor alloc]initWithKey:#"title" ascending:YES];
NSArray *sortDescriptor = [[NSArray alloc]initWithObjects:nameSort, nil];
fetchRequest.sortDescriptors = sortDescriptor;
NSPredicate *p1=[NSPredicate predicateWithFormat:#"startdate > %#", [NSDate date]];
[fetchRequest setPredicate:p1];
[fetchRequest setEntity:entity];
i think you are looking for the above code..
Here is the case. I have 2 models:
Sport (with attribute name and relationShip players)
Player (with attribute name and relationShip sport)
In a ViewController, I wan't to display all players in a UITableView ordered by sport.
When a player name changes, NSFetchedResultsControllerDelegate callbacks are called.
But when the name of a Sport is updated, no NSFetchedResultsControllerDelegate callbacks are called.
Is this a normal behaviour ? How can I know the updates of Sport.name (without creating another NSFetchedResultsController) ?
Thank you and Best Regards.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"sport.name != NULL"];
[fetchRequest setPredicate:predicate];
NSArray *sortDescriptors = [NSArray arrayWithObjects:
[NSSortDescriptor sortDescriptorWithKey:#"sport.name" ascending:YES],
[NSSortDescriptor sortDescriptorWithKey:#"name" ascending:YES],
nil];
[fetchRequest setSortDescriptors:sortDescriptors];
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:#"Player"
inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entityDescription];
fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:#"sport.name"
cacheName:nil];
[fetchRequest release];
fetchedResultsController.delegate = self;
NSError *error = nil;
if ([fetchedResultsController performFetch:&error] == NO) {
ALog(#"Fetch error: %#", [error localizedDescription]);
}
You can using a tip:
If (fetchresultcontroller != nil || isOtherEnthityUpdate)
{
return fetchresultcontroller;
}
And do self.tableview reload from any place where u check other entity. It can be notification center or other tableview.
I am using CoreData and have an Entity ContactList defined, which I created using the XCode xcdatamodel tool (or whatever you call that built-in thing:). I also set the Entity's Class name to "ContactList". I then used rentzsch's mogenerator (http://github.com/rentzsch/mogenerator) to generate my custom class files and added the simple method "toString" to my ContactList class.
The fetchedResultsController callback is pretty much standard and looks as follows:
- (NSFetchedResultsController *)fetchedResultsController {
if (fetchedResultsController_ != nil) {
return fetchedResultsController_;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"ContactList" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
[fetchRequest setFetchBatchSize:20];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"timeStamp" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:#"Root"];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
[aFetchedResultsController release];
[fetchRequest release];
[sortDescriptor release];
[sortDescriptors release];
NSError *error = nil;
if (![fetchedResultsController_ performFetch:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return fetchedResultsController_;
}
Now, when I start my application and try to call that method on a "ContactList" object, which in turn I get from a NSFetchedResultsController, I get
ContactList *contactList = (ContactList *) [self.fetchedResultsController objectAtIndexPath:indexPath];
[contactList toString];
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSManagedObject toString]: unrecognized selector sent to instance 0x6af7bf0'
As the message shows, somehow the NSFetchedResultsController does not return a "ContactList" object, but a more general NSManagedObject instead.
Does anybody know why and how to resolve this???
I had this same problem - Caused because I had previously renamed the Entity's Managed Object subclass. In the Data Modeler, make sure in the Inspector for the entity, both the "Name" and the "Class" are set correctly.
Ok, I figured this it out.
The problem was that the mogenerator script did not add its generated source files to the project's compile target. This way the classes never got compiled and included (I wonder how it worked without any model object classes :)
After adding the .xcdatamodel file to the Target (RightClick on .xcdatamodel file -> Targets -> check your project) everything works now.
For further info go here