Deleting a row from a UITableView fed by an NSFetchedResultsController causes my app to crash.
Error is:
* Assertion failure in -[UITableView _endCellAnimationsWithContext:], /SourceCache/UIKit/UIKit-2903.23/UITableView.m:1330
* Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (1) must be equal to the number of rows contained in that section before the update (1), plus or minus the number of rows inserted or deleted from that section (0 inserted, 1 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).'
I want only swipe-to-delete. My deletion code goes like this:
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
[self.tableView beginUpdates];
SequenceData *sd = [self.fetchedResultsController objectAtIndexPath:indexPath];
[self.managedObjectContext deleteObject:sd];
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];
}
My NSFetchedResultsController is set up just like in Ray Wanderlich's tutorial (http://www.raywenderlich.com/999/core-data-tutorial-for-ios-how-to-use-nsfetchedresultscontroller)
Number of rows is determined by:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
id sectionObjects = [[self.fetchedResultsController sections] objectAtIndex:section];
NSInteger nbObjects = [sectionObjects numberOfObjects];
return nbObjects;
}
It looks like the fetch is not updating (number of rows does not vary). Do I need to fetch myself (isn't this taking care of by the fetch controller)?
There is obviously something basic I am missing here. Don't hesitate to suggest basic answers.
I first implemented this with the controller: didChangeObject: ... method. Error was the same, but some details differed.
Edit
I believe my problem is fixed. Both answers (From CX and Martin) helped me find it. Martin got the answer because of the explanations that helped me understand a little bit better...
Don't call
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
if you use a fetched results controller. Only delete the object with
[self.managedObjectContext deleteObject:sd];
The fetched results controller delegate method didChangeObject: is then called automatically,
and that calls deleteRowsAtIndexPaths:.
So in your case, the row was deleted twice, and that caused the exception.
Note that you don't need beginUpdates/endUpdates here.
If you want to delete a row you need to delete managedObject only.
if (editingStyle == UITableViewCellEditingStyleDelete)
{
[self.managedObjectContext deleteObject:[self.fetchedResultsController objectAtIndexPath:indexPath]];
NSError *error = nil;
if (![self.managedObjectContext save:&error]) {
// handle error
}
}
Deleting the managed object triggers the NSFetchResultController delegate methods, and they will update the tableView.
Edit
You should implement NSFetchResultController delegate method
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath{
switch(type) {
case NSFetchedResultsChangeDelete:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
}
////
default:
break;
}
Because when you work with data source like NSFetchedResultsController, all changes must come from there and your table only reflects them.
Related
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
PFObject *object = [_tdlArray objectAtIndex:(_tdlArray.count - indexPath.row -1)];
[object deleteInBackground];
//found the code for removing a row.
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationLeft];
[tableView reloadData];
[object deleteInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (!succeeded){
[tableView reloadData];
}
}];
}
}
I am able to successfully remove the data, but my app would crash every time I tap on the delete button. I think it has something to do with the [NSArray arrayWithObject:indexPath]
These are the error message
Assertion failure in -[UITableView _endCellAnimationsWithContext:]
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (3) must be equal to the number of rows contained in that section before the update (3), plus or minus the number of rows inserted or deleted from that section (0 inserted, 1 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).'
You want to delete the object then reload the data. Don't dispatch asynchronously the object to be deleted then tell the tableview you're deleting rows because the object has likely not been deleted yet, hence the error you're getting. Use the callback block to update the tableview after the object has been removed that way you can be sure that the object has been deleted. Also if you have a local store of the data that not bound to the data on the server, you need to remove the object from there as well.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
//not sure how you're calculating the index here
PFObject *object = [_tdlArray objectAtIndex:(_tdlArray.count - indexPath.row -1)];
NSMutableArray *mutArray = [_tdlArray mutableCopy];
[mutArray removeObject:object];
_tdlArray = [NSArray arrayWithArray:mutArray];
[object deleteInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (!succeeded){
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationLeft];
[tableView reloadData];
}
}];
}
}
Objective: Using FRC, sort Section's by startDate, an NSDate attribute, but want Today's date Section to appear before Upcoming dates Section.
I followed Apple's code using a transient property sectionIdentifier. Apple's sample code. and started with this project first: OneFRC
I soon realized that this may not be possible with just one FRC (I could be wrong).
Next, I decided to take a stab at this with 3 FRCs: ThreeFRC.
TableView sections now appears in the Order that I want:
Section 0: Today
Section 1: Upcoming
Section 2: Past
However, adding data triggers FRC delegates, and I get the following error:
CoreData: error: Serious application error. An exception was caught from the
delegate of NSFetchedResultsController during a call to
-controllerDidChangeContent:. Invalid update: invalid number of rows in section 0.
The number of rows contained in an existing section after the update (4) must be
equal to the number of rows contained in that section before the update (3), plus
or minus the number of rows inserted or deleted from that section (0 inserted,
0 deleted) and plus or minus the number of rows moved into or out of that section
(0 moved in, 0 moved out). with userInfo (null)
Again, I would love to be able to accomplish my objective with 1 FRC, but I can't seem to figure out how.
I have been trying to resolve this for 4 days now! If this issue doesn't get resolved on SO, I think I may reach out to Apple for Developer support. And in the event that I do, I'll post the resolution here so others can benefit.
Projects are available on Github:
One FRC
Three FRC
EDIT
Thanks to #blazejmar, I was able get rid of the rows error. However, now I get an error when I attempt to add sections.
2014-11-03 16:39:46.852 FRC[64305:60b] CoreData: error: Serious application error.
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 (2) must be
equal to the number of sections contained in the table view before the update (1),
plus or minus the number of sections inserted or deleted (0 inserted, 0 deleted).
with userInfo (null)
Steps to reproduce the error in Three FRC:
1. Launch App ->
2. Tap Generate Data ->
3. Tap View in FRC ->
4. Tap back to the RootVC ->
5. Change the system date to a month from Today ->
6. Tap View in FRC and only one section `Past` should appear. ->
7. Tap `Add Data`.
8. The error should appear in the log.
In your ThreeFRC project there are some issues:
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
[self.tableView beginUpdates];
self.numberOfSectionsInTV = 0;
[self fetchData];
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
[self.tableView reloadData];
[self.tableView endUpdates];
}
You shouldn't use fetchData inside FRC delegate. Methods are called in proper order (before, during and after update) so inside callbacks you have consistent state of context. Also it's not the best idea to use reloadData before endUpdates(it's applying all changes you provided earlier) and reloadData is erasing everything and building it from scratch. This is most likely causing the crash.
Other thing I've spotted that may be buggy is handling of updates. If you have 3 separate FRC without sections you won't get section update callback in FRC delegate. But if some objects appear in one of the FRC's then you should detect that and manually insert them.
Using just reloadData in controllerDidChangeContent would be enough, but this isn't the best solution, as you won't get any animations. The proper way would be to handle all the cases: deleting all objects from one of FRCs (and then deleting section manually from TableView), inserting first object into FRC (then you should create new section at proper indexPath).
i looked at your ThreeFRC project and noticed that in - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView you check which FRCs contain objects and that would determine the number of sections. this makes logical sense, but really confuses the FRC delegate when adding/deleting "sections" (or, when your other FRCs suddenly have objects). For example, you only have a Past section (1 section), but then the data changes such that you now also have a Today section. Since sectionPastFRC or the other FRCs didn't have any section changes, there are no calls to - (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type, and though you should have 2 sections now, there were no calls to add, delete, or move sections. you'd have to update the sections manually somehow, which may be a pain.
here's the workaround i suggest: since you will ALWAYS have at most one section for each FRC, you should just return 3 in - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView. This is so there will no longer be any problem in adding/deleting a section because they were all already there. Anyway, if, for example, the Today section has no objects, just return 0 in - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section. Just make sure that in - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section, if fetchedObjects==0, return nil, so that it also won't display the section header if that section has no objects. And in your FRC delegate didChangeObject, just always adjust the indexPath and newIndexPath before performing changes on the tableView.
note that this workaround will only work if you already know the maximum number of sections that the FRCs (except the last FRC) will need. it is NOT a solution for all implementations of multiple FRCs in a single table view. i've actually used this solution in a project where i had 2 FRCs for one tableView, but the first FRC would only always take up 1 section, while the second FRC could have any number of sections. i always just had to adjust the sections +1 for changes in the second FRC.
i've actually tried applying the changes i mentioned above into your code, and haven't been getting errors. here are the parts i changed in the UITableViewDataSource:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 3;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSInteger rows = 0;
switch (section) {
case 0:
{
rows = [[self.sectionTodayFRC fetchedObjects]count];
break;
}
case 1:
{
rows = [[self.sectionUpcomingFRC fetchedObjects]count];
break;
}
case 2:
{
rows = [[self.sectionPastFRC fetchedObjects]count];
break;
}
}
NSLog(#"Section Number: %i Number Of Rows: %i", section,rows);
return rows;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
NSString *header;
switch (section) {
case 0:
{
if ([[self.sectionTodayFRC fetchedObjects]count] >0)
{
header = #"Today";
}
break;
}
case 1:
{
if ([[self.sectionUpcomingFRC fetchedObjects]count] >0)
{
header = #"Upcoming";
}
break;
}
case 2:
{
if ([[self.sectionPastFRC fetchedObjects]count] >0)
{
header = #"Past";
}
break;
}
}
return header;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
}
Meeting *meeting;
switch (indexPath.section) {
case 0:
if ([[self.sectionTodayFRC fetchedObjects]count] > 0)
{
meeting = [[self.sectionTodayFRC fetchedObjects] objectAtIndex:indexPath.row];
}
break;
case 1:
if ([[self.sectionUpcomingFRC fetchedObjects]count] > 0)
{
meeting = [[self.sectionUpcomingFRC fetchedObjects] objectAtIndex:indexPath.row];
}
break;
case 2:
if ([[self.sectionPastFRC fetchedObjects]count] > 0)
{
meeting = [[self.sectionPastFRC fetchedObjects] objectAtIndex:indexPath.row];
}
break;
}
cell.textLabel.text = meeting.title;
return cell;
}
and for the NSFetchedResultsControllerDelegate:
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {
NSLog(#"Inside didChangeObject:");
NSIndexPath *modifiedIndexPath;
NSIndexPath *modifiedNewIndexPath;
if (controller == self.sectionTodayFRC)
{
modifiedIndexPath = [NSIndexPath indexPathForRow:indexPath.row inSection:0];
modifiedNewIndexPath = [NSIndexPath indexPathForRow:newIndexPath.row inSection:0];
}
else if (controller == self.sectionUpcomingFRC)
{
modifiedIndexPath = [NSIndexPath indexPathForRow:indexPath.row inSection:1];
modifiedNewIndexPath = [NSIndexPath indexPathForRow:newIndexPath.row inSection:1];
}
else if (controller == self.sectionPastFRC)
{
modifiedIndexPath = [NSIndexPath indexPathForRow:indexPath.row inSection:2];
modifiedNewIndexPath = [NSIndexPath indexPathForRow:newIndexPath.row inSection:2];
}
switch(type) {
case NSFetchedResultsChangeInsert:
[self.tableView insertRowsAtIndexPaths:#[modifiedNewIndexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
break;
case NSFetchedResultsChangeDelete:
NSLog(#"frcChangeDelete");
[self.tableView deleteRowsAtIndexPaths:#[modifiedIndexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
break;
case NSFetchedResultsChangeUpdate:
NSLog(#"frcChangeUpdate");
[self.tableView reloadRowsAtIndexPaths:#[modifiedIndexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
break;
case NSFetchedResultsChangeMove:
NSLog(#"frcChangeDelete");
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:modifiedIndexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:modifiedNewIndexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
break;
}
}
i hope this helps someone!
I have a table view backed by NSFetchedResultsController.
The situation is as follows.
I have implemented "swipe to delete" a row. Delegate methods of NSFetchedResultsController and those of table view's are called. But the thing is, I don't want to delete a Core Data Entity, I just want to mark its boolean flag since I need this entity in other parts of application, and want table view to delete row with animation. However, according to error messages it is against logic like if you are updating an entity, update a row, not delete it.
Is there any elegant way to achieve it? Or the only option is reload the table?
Implementation of swipe to delete
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
Issue *issue = [self.fetchedResultsController objectAtIndexPath:indexPath];
[issue setHasBeenDeleted:[NSNumber numberWithBool:YES]];
[issue setPurchased:[NSNumber numberWithBool:NO]];
[self.managedObjectContext saveToPersistentStore:nil];
}
}
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id) anObject
atIndexPath:(NSIndexPath *)indexPath
forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath *)newIndexPath
...
case NSFetchedResultsChangeUpdate:
[self.tableView deleteRowsAtIndexPaths:#[indexPath]
withRowAnimation:UITableViewRowAnimationAutomatic];
break;
NSFetchedResultsControllerDelegate
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
// The fetch controller has sent all current change notifications, so tell the table view to process all updates.
[self.tableView endUpdates];
}
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
// The fetch controller is about to start sending change notifications, so prepare the table view for updates.
[self.tableView beginUpdates];
}
Error message
CoreData: error: Serious application error. An exception was caught from the delegate of NSFetchedResultsController during a call to -controllerDidChangeContent:. Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (5) must be equal to the number of rows contained in that section before the update (4), plus or minus the number of rows inserted or deleted from that section (0 inserted, 1 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out). with userInfo (null)
In the fetch request you are giving to your FRC, include this boolean value as a part of your predicate. For example, [NSPredicate predicateWithFormat:#"shouldDisplay = YES"]. Then whenever you want to remove are particular object, just set your shouldDisplay (or whatever you call it) to NO, and you will receive an FRC delegate callback for controller:
didChangeObject: with a NSFetchedResultsChangeDelete change type. In this callback, you will do the following.
NSIndexPath *indexPath = [controller indexPathOfObject:anObject];
[tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
Your object will remain in core data, but it will be filtered out of this particular table view because of your predicate on the boolean flag.
EDIT: You also need to implement two of the FRC delegate methods to notify your table view to begin and end updates. This will correct the error you are receiving. Like so:
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
[self.tableView beginUpdates];
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
[self.tableView endUpdates];
}
I couldn't get Pat Goley's solution to work, though perhaps I didn't implement it properly. After some experimentation, the solution I came up with is contained in a single callback method:
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle != .Delete { return }
let obj = fetchedResultsController.objectAtIndexPath(indexPath) as! Entity
obj.needsDeletion = true
try! managedObjectContext.save()
try! fetchedResultsController.performFetch()
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
}
This has the disadvantage that performFetch() must be called, but without it, it's crash city. For very large recordsets, an approach similar to Pat's is probably best.
I've got an UITableView that uses an NSFetchedResultsController as it's data source. I use one of the fields for my sections. I enabled row deletion (using the swipe gesture). Works just fine.
The problem comes up when I delete the last row of a section. It does not crash on me but the console shows the following message:
CoreData: error: Serious application error. An exception was caught
from the delegate of NSFetchedResultsController during a call to
-controllerDidChangeContent:. Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section
after the update (1) must be equal to the number of rows contained in
that section before the update (1), plus or minus the number of rows
inserted or deleted from that section (0 inserted, 1 deleted) and plus
or minus the number of rows moved into or out of that section (0 moved
in, 0 moved out). with userInfo (null)
This is all pretty basic stuff:
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
[[self tableView] beginUpdates];
}
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath *)newIndexPath {
UITableView *tableView = self.tableView;
switch(type) {
…
case NSFetchedResultsChangeDelete:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeUpdate:
[tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
[self configureCell:[tableView cellForRowAtIndexPath:indexPath]
atIndexPath:indexPath];
break;
…
}
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
[[self tableView] endUpdates];
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the row from the data source
FHClass *foo = [[self fetchedResultsController] objectAtIndexPath:indexPath];;
[[self managedObjectContext] deleteObject:meal];
[self saveContext];
}
}
If I check -numberOfRowsInSection: I see that the number is steadily decreasing when deleting rows. Even to the point where it says 0.
What did I miss? Any hints? Stupid mistake on my part? ;-)
You should implement controller:didChangeSection:atIndex:forChangeType: to deal with section changes and then request the table view to deleteSections:.
i use NSFetchedResultsController to populate tableview and every thing is ok(adding, deletng..).. But whenever i try to remove last row from tableview(when there is only one row in tablview) the app crashes with the log below:
*** Assertion failure in -[UITableView _endCellAnimationsWithContext:], /SourceCache/UIKit_Sim/UIKit-1914.84/UITableView.m:1037
2012-08-03 16:32:39.667 MackaWithCoreData[793:15503] CoreData: error: Serious application error. An exception was caught from the delegate of NSFetchedResultsController during a call to -controllerDidChangeContent:. Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (1) must be equal to the number of rows contained in that section before the update (1), plus or minus the number of rows inserted or deleted from that section (0 inserted, 1 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out). with userInfo (null)
As far as i see, after deleting the object when the delegate methods try to reload the tableview since the fetchedResultsController is null it causes a crash.. How to handle this issue? Thanks in advance
EDIT Implementation of controller controllerDidChangeContent:... method is below
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath *)newIndexPath
{
switch(type)
{
case NSFetchedResultsChangeInsert:
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
XLog(#"the row removed from tv");
break;
case NSFetchedResultsChangeUpdate:
// [self configureCell:[self.tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
break;
case NSFetchedResultsChangeMove:
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
XLog(#" object deleted from fetchedresultscontroller");
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
After a few days i decided a different logic to solve this issue. I firstly control the count of fetchedObjects on fetchedResultsController, if the count is greater than 1 i call deletion & saving delegates on database, but if the count is equal to 1, just push to a different viewController. On that vc too, i'll disable navigation controller back to this view controller untill a new message comes. And when a new message comes, i will firstly delete the object on entity and than add the message..
Here is some pseudo code & algorithm:
if ([[self.fetchedResultsController fetchedObjects]count] >1) {
callDeletionDelegateForDatabase;
}else{
declare a viewController object;
pushToThatVC;
}
// on the class of target vc
hide_back_navigation_back_to_theViewControllerYouCameFrom;
//when a new message comes from web service
reset Entitiy(the property we need its value)
show_navigation_Controller_To_theViewControllerOfMessages...
I posted this stupidly coded pseudo to help others who faces the same situation, if anyone knows a better approach, please write down.
Below is my code:
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
if ([[self.fetchedResultsController fetchedObjects]count] >1) {
NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
[context deleteObject:[self.fetchedResultsController objectAtIndexPath:indexPath]];
// Save the context.
NSError *error = nil;
if (![context save:&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();
}
}else {
BNT_DetailViewController *vc=[BNT_DetailViewController new];
[self.navigationController pushViewController:vc animated:YES];
XLog(#"You Cant delete this");
}
}
}