Custom Core Data SectionNameKeyPath
Solution 1
The following should work: Implement the periodYear
method (which will be used
as "section name key path") in a class extension of your managed object subclass:
@interface Event (AdditionalMethods)
- (NSString *)periodYear;
@end
@implementation Event (AdditionalMethods)
- (NSString *)periodYear {
return [self.acctPeriod substringToIndex:4];
}
@end
Make sure that acctPeriod
is used as the first (or only) sort descriptor for the fetch request:
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"acctPeriod" ascending:YES];
NSArray *sortDescriptors = @[sortDescriptor];
[fetchRequest setSortDescriptors:sortDescriptors];
Use periodYear
as sectionNameKeyPath
for the fetched results controller:
NSFetchedResultsController *_fetchedResultsController = [[NSFetchedResultsController alloc]
initWithFetchRequest:fetchRequest
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:@"periodYear"
cacheName:nil];
_fetchedResultsController.delegate = self;
self.fetchedResultsController = _fetchedResultsController;
And finally add the default titleForHeaderInSection
method:
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section];
return [sectionInfo name];
}
Alternatively, you can define periodYear
as transient attribute of the managed object.
It will also not be stored in the database in that case, but can be implemented in a way that the value is calculated on demand and cached.
The DateSectionTitles sample project from the Apple Developer Library demonstrates how this works.
Solution 2
I recommend against using a transient property as the sectionNameKeyPath
as it will result in faulting all objects obtained by the fetch request just so the property could be used as the section path.
You better define a persistent property of year
and use it as your sectionNameKeyPath
.
set you FRC sectionNameKeyPath
to year
like so:
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:@"year"
cacheName:nil/*your chioce*/];
to display the section name as a title in the table, implement:
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
id<NSFetchedResultsSectionInfo> sec = [self.fetchedResultsController sections][section];
return [sec name];
}
theDVUSone
Updated on July 25, 2022Comments
-
theDVUSone almost 2 years
I am new at core data and am trying to figure out how to create a custom
sectionNameKeyPath
in myNSFetchedResultsController
. I have a managed object with an attribute calledacctPeriod
. It is aNSString
. I want to create sections based on the first 4 characters of this field. The first 4 characters represent the year of the accounting period and doesn't need to be saved.I have gone through this site and have seen posts about transient attributes but I can't seem to get them to work. Basically I want this and then assign
periodYear
for mysectionNameKeyPath
.@dynamic periodYear; -(NSString *)periodYear { return [self.acctPeriod substringToIndex:4]; }
Any help would be appreciated.
**UPDATE: Using Martin R. answer, I was able to get it to work as expected.
- (NSFetchedResultsController *)fetchedResultsController { if (_fetchedResultsController != nil) { return _fetchedResultsController; } NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init]; // Edit the entity name as appropriate. NSEntityDescription *entity = [NSEntityDescription entityForName:@"Billing" 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:@"acctPeriod" ascending:NO]; NSArray *sortDescriptors = @[sortDescriptor]; //Predicate NSPredicate *pred = [NSPredicate predicateWithFormat:@"clients = %@", self.client]; NSLog(@"%@",pred); //[fetchRequest setResultType:NSDictionaryResultType]; //[fetchRequest setReturnsDistinctResults:YES]; [fetchRequest setPredicate:pred]; [fetchRequest setSortDescriptors:sortDescriptors]; NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:@"periodYear" cacheName:nil]; aFetchedResultsController.delegate = self; self.fetchedResultsController = aFetchedResultsController; NSError *error = nil; if (![self.fetchedResultsController performFetch:&error]) { NSLog(@"Unresolved error %@, %@", error, [error userInfo]); abort(); } return _fetchedResultsController; }
-
theDVUSone about 11 yearsOK, that worked great. I tried this before but the problem is that I'm trying only to load 1 of my entities, acctPeriod, and sort it by periodYear. So I was using [fetchRequest setPropertiesToFetch:[NSArray arrayWithObjects:[entityProperties objectForKey:@"acctPeriod"],nil]]; which doesn't seem to work. I tried to put periodYear as entityProperty but I know that it's not. I get this error: 'Invalid keypath periodYear passed to setPropertiesToFetch:' Any thoughts?
-
Martin R about 11 years@theDVUSone: If you use
propertiesToFetch
then you probably have to defineperiodYear
as transient attribute and add it to thepropertiesToFetch
. I did not try it, but that is what I assume. -
InitJason about 10 yearsI was making the mistake of declaring a @property that implemented a getter. This solution more or less pointed out that it should just be a method declaration.