Welcome to the Treehouse Community
Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.
Looking to learn something new?
Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.
Start your free trialRashii Henry
16,433 PointsIns and Outs of Deleting Data
at about 8 minutes into the video and its time to run the application. I catch an exception in my -controllerDidChangeContent method. here's what the error says: "An exception was caught from the delegate of NSFetchedResultsController during a call to -controllerDidChangeContent:. Invalid update: invalid number of sections. The number of sections contained in the table view after the update (1) must be equal to the number of sections contained in the table view before the update (0), plus or minus the number of sections inserted or deleted (0 inserted, 0 deleted). with userInfo (null)"
i have the exact code that Ash Furrow has. The only line listed in that method is the endUpdates method call on the tableView property. I've stepped back a couple of steps and even tried implementing the switch casing one by one and running the app right after. The same error persists.c I only get this error when I'm running it on my device and i either try to add a row or either delete a row.
6 Answers
Ash Furrow
Treehouse Guest TeacherI think this is covered in the subsequent video, but I'll check. Is this happening only when you delete the last item, or any item? There's one more method we need to implement:
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id )sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {
switch(type) {
case NSFetchedResultsChangeInsert:
[self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
Ash Furrow
Treehouse Guest TeacherYeah, checkout the code I insert at 8:25 of the video you mentioned.
Ash Furrow
Treehouse Guest TeacherHi Rashii – you're saying that the error only happens when you're running on a device, not on the simulator?
Rashii Henry
16,433 PointsThanks for the quick response but the error happens whether i run it on the device or in the simulator. if i take a step back and comment out the code for the controllerDidChangeObject AtIndexPath ForChangeType NewIndexPath
the console catches another exception "Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM objectAtIndex:]: index 1 beyond bounds [0 .. 0]'"
then if i go back even further. back to when we had only one method and we called the reload data. Everything works fine.
Ash Furrow
Treehouse Guest TeacherCan you post the full contents of your view controller?
Rashii Henry
16,433 PointsSure, could you correctly tell me how to format the code? every time i try it only blocks out certain sections.
Ash Furrow
Treehouse Guest TeacherYou should be able to put everything between three back ticks – check out the Markdown Cheatsheet.
Rashii Henry
16,433 Points#import "EntryListTableViewController.h"
#import "CoreDataStack.h"
#import "DiaryEntry.h"
@interface EntryListTableViewController () <NSFetchedResultsControllerDelegate>
@property (nonatomic, strong) NSFetchedResultsController *fetchedResultsController;
@end
@implementation EntryListTableViewController
-(NSFetchRequest *)entryListfetchRequest
{
//this method can be tested independently of actually fetching data.
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"DiaryEntry"];
fetchRequest.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"date" ascending:NO]];
return fetchRequest;
}
-(NSFetchedResultsController *)fetchedResultsController
{
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
//grab the core data stack
CoreDataStack *coreDataStack = [CoreDataStack defaultStack];
//create a fetch request that calls the entryListRequest method.
NSFetchRequest *fetchRequest = [self entryListfetchRequest];
_fetchedResultsController = [[NSFetchedResultsController alloc]initWithFetchRequest:fetchRequest managedObjectContext:coreDataStack.managedObjectContext sectionNameKeyPath:@"sectionName" cacheName:nil];
_fetchedResultsController.delegate = self;
return _fetchedResultsController;
};
-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
//grab the section info from our fetched results controller.
id <NSFetchedResultsSectionInfo> sectionInfo = [self.fetchedResultsController sections][section];
return [sectionInfo name];
}
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:UITableViewStyleGrouped];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Uncomment the following line to preserve selection between presentations.
// self.clearsSelectionOnViewWillAppear = NO;
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
[self.fetchedResultsController performFetch:nil];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return self.fetchedResultsController.sections.count;
}
#pragma mark Number of rows in section.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
id <NSFetchedResultsSectionInfo> sectionInfo = [self.fetchedResultsController sections][section];
return [sectionInfo numberOfObjects];
}
#pragma mark Cell For row at indexPath
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];
// Configure the cell...
DiaryEntry *entry = [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = entry.body;
return cell;
}
#pragma mark tableViewCell Editing style
-(UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
//this i one of the methods required to swipe to delete.
return UITableViewCellEditingStyleDelete;
}
-(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
//to delete an entry we first have to get the item we want to delete, in this case its the diary entry.
DiaryEntry *entry = [self.fetchedResultsController objectAtIndexPath:indexPath];
CoreDataStack *coreDataStack = [CoreDataStack defaultStack];
//select our coreDataStack and delete the entry from the stack.
[[coreDataStack managedObjectContext]deleteObject:entry];
[coreDataStack saveContext];
}
//if the content has been changed save it and reload the data in the table view.
-(void)controllerWillChangeContent:(NSFetchedResultsController *)controller
{
[self.tableView beginUpdates];
}
-(void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath
{
//this method is called whenever a row is inserted, removed, updated, moved, or changed.
//we're going to switch on the type to actually determined what's happening.
switch (type) {
//if the type is NSFetchedResultsChangeInsert we will insert a new row at the new indexPath.
case NSFetchedResultsChangeInsert:
[self.tableView insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
break;
//if the type is NSFetchedResultsChangeDelete we will remove a row at the indexPath.
case NSFetchedResultsChangeDelete:
[self.tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
break;
//if the type is NSFetchedResultsChangeUpdate we will reload a row at the current indexPath.
case NSFetchedResultsChangeUpdate:
[self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
break;
}
}
-(void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
[self.tableView endUpdates];
// [self.tableView reloadData];
}
Ash Furrow
Treehouse Guest TeacherA backtick is actually the ` key – usually next to the 1 on US keyboards. Just put them on their own line, like this:
code
Rashii Henry
16,433 PointsAsh Furrow, thanks its updated.
Rashii Henry
16,433 PointsI'm not even going to even try to think of how i missed the fact that you implemented a whole new method. bBut it was happening whenever i added or tried to delete an item. My application works fine now. Thanks a lot man!
Ash Furrow
Treehouse Guest TeacherAsh Furrow
Treehouse Guest TeacherHi Rashii – you're saying that the error only happens when you're running on a device, not on the simulator?