Core Data family members grouped by surname - ios

This is my first attempt on Core Data so I would like your guidance.
My example project is quite simple, I would like to create an iOS app that displays a list of people. What I am looking for is to group all members of the same family together. Some people in the list do not belong to a "family". So, UITableView is going to be a mixture of groups and rows.
Here is my Model so far.
and here I am adding data in the context
Family *newFamily = [NSEntityDescription insertNewObjectForEntityForName:#“Family” inManagedObjectContext:self.managedObjectContext];
[newFamily setSurname:[dict objectForKey:#“surname”]];
NSMutableSet *members = [newTeam mutableSetValueForKey:#"members"];
for (NSDictionary *member in [dict objectForKey:#"members"]) {
Member *newMember = [NSEntityDescription insertNewObjectForEntityForName:#"Member" inManagedObjectContext:self.managedObjectContext];
[newMember setFirstName:[member objectForKey:#"firstName"]];
[newMember setAge:[member objectForKey:#“age”]];
[members addObject:newMember];
}
//Save context ...
In UITableViewController I am able to read and display just families'/groups' name. I am confused on how do you display all the objects of a section.
- (NSFetchedResultsController *)fetchedResultsController
{
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Family" 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:#"surname" ascending:NO];
[fetchRequest setSortDescriptors:#[sortDescriptor]];
// 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:#"surname" cacheName:#"Master"];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
return _fetchedResultsController;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [[self.fetchedResultsController sections] count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// I DON'T KNOW WHAT TO DO HERE ...
// return 0;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
id <NSFetchedResultsSectionInfo> sectionInfo = [self.fetchedResultsController sections][section];
return [sectionInfo name];
}

First, you need to amend your model to include the inverse relationship, from Member to Family, which presumably is "to-one". Name that relationship "family". (This inverse relationship is needed in this case, see below, but you should almost always include inverses for all relationships, regardless of whether you think you need them.)
Next, amend your FRC configuration as follows: the underlying entity should be Member, since each row of your tableView will represent a Member object. The sectionNameKeyPath should be "family.surname" so the FRC will automatically put the Member objects into sections based on the surname attribute of the related Family object. It is important that the sort order for the FRC matches up with the sections (ie all Member objects in the same section must be sorted together), so amend the sort descriptors to use family.surname (you could then sort by firstName if desired):
- (NSFetchedResultsController *)fetchedResultsController
{
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Member" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
// Set the batch size to a suitable number.
[fetchRequest setFetchBatchSize:20];
// Edit the sort key as appropriate.
NSSortDescriptor *surnameSortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"family.surname" ascending:NO];
NSSortDescriptor *firstNameSortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"firstName" ascending:NO];
[fetchRequest setSortDescriptors:#[surnameSortDescriptor, firstNameSortDescriptor]];
// 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:#"family.surname" cacheName:#"Master"];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
return _fetchedResultsController;
}
Finally, there is boilerplate code for linking an FRC to a tableView (see the Apple Docs here). The piece you are missing is:
- (NSInteger)tableView:(UITableView *)table numberOfRowsInSection:(NSInteger)section {
if ([[self.fetchedResultsController sections] count] > 0) {
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section];
return [sectionInfo numberOfObjects];
} else
return 0;
}
Your configureCell method will likewise need to determine the correct Member object to display using
Member *currentMember = [self.fetchedResultsController objectAtIndexPath:indexPath];
Note that the FRC will log a warning message if there are Member objects that are not in a Family, since the sectionName will be nil. But it will group these all together - you might want to amend the titleForHeaderInSection to use a different name for that section.
Also, note that the FRC's sections are determined by inspecting the family.surname for each Member object. If there are Family objects that have no members, those Family objects will not appear in the table view (i.e. no empty sections).

Related

Problems with NSFetchedResultsController in iOS7?

I am using NSFetchedResultsController to load some data into UITableView. No matter how many objects I save, it only loads the first one. If I delete that one object from the UITableView and reload it, sometimes it will show one or more of the other objects I've saved. It's very unpredictable.
The strange thing is the code I was using worked fine on the iOS6 SDK. I know that the issue is with the NSFetchedResultsController because when I make a fetch request with NSFetchRequest, the data that is returned is correct. Here is the code;
- (void) viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
NSError *error;
if (![[self fetchedResultsController] performFetch:&error])
{
exit(-1);
}
}
- (void)viewDidUnload
{
self.fetchedResultsController = nil;
}
- (NSFetchedResultsController *)fetchedResultsController
{
if (_fetchedResultsController != nil)
{
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"HatInfo" inManagedObjectContext:[self.appDelegate managedObjectContext]];
[fetchRequest setEntity:entity];
NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:#"lastSaved" ascending:NO];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]];
[fetchRequest setFetchBatchSize:9];
NSArray *fetchedObjects = [[self.appDelegate managedObjectContext] executeFetchRequest:fetchRequest error:nil];
//returns the correct amount of objects
NSLog(#"FetchedObjects: %lu", (unsigned long)[fetchedObjects count]);
NSFetchedResultsController *theFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:[self.appDelegate managedObjectContext] sectionNameKeyPath:nil
cacheName:#"Root"];
self.fetchedResultsController = theFetchedResultsController;
_fetchedResultsController.delegate = self;
return _fetchedResultsController;
}
- (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
id sectionInfo = [[_fetchedResultsController sections] objectAtIndex:section];
//returns the incorrect amount
return [sectionInfo numberOfObjects];
}
This kind of unexpected behavior can happen if you used the same cacheName in other part of core data code (for example: another fetchedResultsController in your app.
You can try to change the cache name to something different or nil and see what happens.
Add this line before you start fetch :
[NSFetchedResultsController deleteCacheWithName:<< catch name>>];

numberOfRowsInSection: for a specific attribute

I have a UITableView that is populated with data from core data and I currently have it set up so that it adjusts the size of the table based on the number of objects in an entity. My problem is that I know have two attributes in this entity. Now the tableview has a bunch of extra cells because it is creating one for everything in the entity, not just creating one per object in a specific attribute. So I would like to set numberOfRowsInSection: based on amount of objects in an attribute, not the whole entity. How would I do this?
How I am currently doing it:
id sectionInfo =
[[_fetchedResultsController sections] objectAtIndex:section];
return [sectionInfo numberOfObjects];
EDIT:
This is what I have tried:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
// id sectionInfo =
// [[_fetchedResultsController sections] objectAtIndex:section];
// return [sectionInfo numberOfObjects];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSManagedObjectContext *context = [self managedObjectContext];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"FeedEntity" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
fetchRequest.predicate = [NSPredicate predicateWithFormat:#"imageData != nill"];
NSFetchedResultsController *theFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:context sectionNameKeyPath:nil cacheName:nil];
id sectionInfo =
[[theFetchedResultsController sections] objectAtIndex:section];
return [sectionInfo numberOfObjects];
}
But this results in this error:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'An instance of NSFetchedResultsController requires a fetch request with sort descriptors'
Ok, well thats an easy solve. Will there always be an image for every single string? Why not just return the yourStringDataArray.length in the -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section method? This way you will only get the amount of cells needed to display your total amount of strings in your array.
so you have one entity with two attributes. And you need to filter the objects to show for the tableview. I think you need to use NSFetchRequest and NSPredicate to filter your objects by checking the attribute of your object.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSManagedObjectContext *context = [your context];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"FeedEntity" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
fetchRequest.predicate = [NSPredicate predicateWithFormat:#"imageData = nil"];
request.sortDescriptors = [NSArray arrayWithObject:[[NSSortDescriptor alloc] initWithKey:#"text" ascending:NO]];
NSFetchedResultsController *fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:context sectionNameKeyPath:nil cacheName:nil];

Show sections with core data

I want to code a todo app and display 2 sections. 1 for entrys, that have to be done and 1 for already done things.
I want something like that:
Things not done yet
Todo 1
Todo 2
Things already done
Todo 3
Todo 4
At the moment I have 1 section for the undone todos.
My code for the table view at the moment looks like this
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
NSArray *array = [[NSArray alloc] initWithObjects:#"To do",#"done",nil];
return [array objectAtIndex:section];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [[self.fetchedResultsController sections] count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
id <NSFetchedResultsSectionInfo> sectionInfo = [self.fetchedResultsController sections][section];
return [sectionInfo numberOfObjects];
}
And my code for the fetchedresultscontroller looks like this:
- (NSFetchedResultsController *)fetchedResultsController
{
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
NSManagedObjectContext* context = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription* entity = [NSEntityDescription entityForName:#"Todo" inManagedObjectContext:context];
[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:#"timestamp" ascending:YES];
NSArray *sortDescriptors = #[sortDescriptor];
[fetchRequest setSortDescriptors:sortDescriptors];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"done == %#", #(NO)];
[fetchRequest setPredicate:predicate];
// 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:#"done" cacheName:nil];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
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;
}
Objects with the key "NO" are not done and with the key "YES" are done. But I don't know how to display both values in different sections.
Edit
I found the solution to my problem…
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:#"done" cacheName:nil];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
That solved this problem. But now sometimes an undone item shows up at done.
Or if there are two done items they appear under "Todo"
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
NSArray *array = [[NSArray alloc] initWithObjects:#"Todo",#"Done",nil];
return [array objectAtIndex:section];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [[self.fetchedResultsController sections] count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
id <NSFetchedResultsSectionInfo> sectionInfo = [self.fetchedResultsController sections][section];
return [sectionInfo numberOfObjects];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
Thats my actual code for that…
Now it looks like that. Undone todo unchecked, done todo checked. http://img.xnmn.de/i/2353ad.png
But if I check undone todo as done, they both appear at "todo" http://img.xnmn.de/i/5edff4.png
You're code looks almost right. Since you set the sectionKeyPath to "done", the fetched results controller will automatically create the sections for you. You could add another sort descriptor for the "done" attribute to sort the entries according to their section.
What you have to do is to remove the NSPredicate. In you current code, you're filtering out all entries with "done == YES", but since you have your two sections, you want the results controller to return all entries.
So basically you just need to remove the
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"done == %#", #(NO)];
[fetchRequest setPredicate:predicate];
Of course you'd also have to implement the – tableView:cellForRowAtIndexPath: method
If i get your Code right you already created the section which is fine.
- (UITableViewCell *)tableView:(UITableView *)inTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *identifier = #"Entry";
UITableViewCell *cell = [inTableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:identifier];
}
YourObject *cellEntry = [_fetchedResultsController objectAtIndexPath:indexPath];
//init cell
}
This should choose the right entry for the right section otherwise the line where you set the section is wrong.
The index path chooses the right section.

Custom Core Data SectionNameKeyPath

I am new at core data and am trying to figure out how to create a custom sectionNameKeyPath in my NSFetchedResultsController. I have a managed object with an attribute called acctPeriod. It is a NSString. 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 my sectionNameKeyPath.
#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;
}
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.
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];
}

UITableView sectioned with sort order not sectioning correctly

So I am trying to get a UITableView to show a list of objects by section (thing.title), but list them in descending order by date.
The table is is split into sections, which are labeled correctly (section headers are the different thing titles).
But the objects in each section are only half correct. The objects in each section are listed in descending order, but some sections contain data that should be in other sections.
An example of what is happening:
<Header> Big Title Name
<data><Big Title><id=1></data>
<data><Big Title><id=4></data>
**<data><Small Title><id=6></data>** <-- should not be in this section
<Header> Small Title Name
<data><Small Title><id=11></data>
<data><Big Title><id=23></data> <-- should not be in this section
**<data><Small Title><id=66></data>**
Here is part of my code:
- (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:#"AReads" inManagedObjectContext:[NSManagedObjectContext defaultContext]];
[fetchRequest setEntity:entity];
// Set the batch size to a suitable number.
[fetchRequest setFetchBatchSize:20];
// Sort using the timeStamp property..
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"timeStamp" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
// Use the sectionIdentifier property to group into sections.
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:[NSManagedObjectContext defaultContext] sectionNameKeyPath:#"sessionTitle" cacheName:#"Root"];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
return fetchedResultsController;
}
- (NSString*) tableView:(UITableView *)tableView titleForHeaderInSection: (NSInteger)section
{
//return [(Sessions*)[masterSessions objectAtIndex: section] title];
id <NSFetchedResultsSectionInfo> theSection = [[fetchedResultsController sections] objectAtIndex:section];
NSString *theTitle = [theSection name];
return theTitle;
}
The key used as sectionNameKeyPath of a fetched results controller and the key used in the first sort descriptor must either be the same keys or generate the same relative ordering. So you cannot use sessionTitle as sectionNameKeyPath and a completely different key timeStamp as sort descriptor for the sections.
In your case, it is probably the best to use timeStamp as sectionNameKeyPath and in the sort descriptor. This will ensure that all entries are correctly grouped into sections.
To display the sessionTitle instead of the timeStamp in the section header, you can modify titleForHeaderInSection:
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.controller sections] objectAtIndex:section];
return [[[sectionInfo objects] objectAtIndex:0] sessionTitle];
}

Resources