App is crashing when deleting from UITableView due to self referencing connection - iphone

I'm brand new to iPhone development (and first question posted here) and am sort of stuck with Core Data and Table Views.
In short, my app is crashing when I delete a row from my UITableView due to NSFetchedResultsChangeUpdate being called on a record that has already been removed due to a cascade delete on a self referring table.
Here is a description of the data model:
There are two Entities; Person and Connection.
Person contains name (String), connections (To Many Relationship to Connection->source, cascade delete rule) and connectedby (To Many Relationship to Connection->connection, cascade delete rule)
Connection contains relationship (String), source (Relationship to Person->connections, nullify delete rule) and connection (Relationship to Person->connectedby, nullify delete rule)
The idea being that there are two people connected by a relationship (eg Mother or Son)
In my TableViewController I implement the following:
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
[self.tableView beginUpdates];
}
and
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {
UITableView *tableView = self.tableView;
Person *person = nil;
switch(type) {
case NSFetchedResultsChangeInsert:
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeUpdate:
person = (Person *)[fetchedResultsController objectAtIndexPath:indexPath];
[self configureCell:(PersonTableViewCell *)[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
break;
case NSFetchedResultsChangeMove:
person = (Person *)[fetchedResultsController objectAtIndexPath:indexPath];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
and
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
[self.tableView endUpdates];
}
Here are the sample records I created for testing this:
Person *person1 = [NSEntityDescription insertNewObjectForEntityForName:#"Person" inManagedObjectContext:self.managedObjectContext];
[person1 setName:[NSString stringWithFormat: #"Tommy"]];
Person *person2 = [NSEntityDescription insertNewObjectForEntityForName:#"Person" inManagedObjectContext:self.managedObjectContext];
[person2 setName:[NSString stringWithFormat: #"Jane"]];
Connection *connection = [NSEntityDescription insertNewObjectForEntityForName:#"Connection" inManagedObjectContext:managedObjectContext];
[connection setConnection:person2];
[connection setSource:person1];
[connection setRelationship:[NSString stringWithFormat:#"Mother"]];
Connection *connection2 = [NSEntityDescription insertNewObjectForEntityForName:#"Connection" inManagedObjectContext:managedObjectContext];
[connection2 setConnection:person1];
[connection2 setSource:person2];
[connection2 setRelationship:[NSString stringWithFormat:#"Son"]];
When I delete the record at indexPath[0,0] i.e. Jane in this example since the view is sorted by name, I generate the following error:
2010-10-19 16:09:01.461 HelpMe[6324:207]
Serious application error.
Exception was caught during Core Data change processing.
This is usually a bug within an observer of NSManagedObjectContextObjectsDidChangeNotification.
*** -[NSMutableArray objectAtIndex:]: index 1 beyond bounds [0 .. 0] with userInfo (null)
Detected an attempt to call a symbol in system libraries that is not present on the iPhone:
_Unwind_Resume called from function -[NSManagedObjectContext(_NSInternalChangeProcessing) _processRecentChanges:] in image CoreData.
The delete seems to correctly generate a NSFetchedResultsChangeDelete for indexPath [0,0] but also then immediately generates a NSFetchedResultsChangeUpdate for [0,1] which no longer exists since [0,1] is seemingly now in [0,0] after the delete.
Without the associated Connection record, it deletes fine.
I can seemingly work around this by simply calling [self.tableView reloadData] on controllerDidChangeContent instead of implementing begin/end updates and didChangeOnject: but I do not believe this is the proper way to handle this.
I appreciate any help anyone can offer.

I resolved a similar issue this by providing a non-nil sectionNameKeyPath to NSFetchedResultsController (in initWithFetchRequest).
I then avoided sections by using:
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
return #"";
}
Very annoying.
FYI, my issue was this error:
Serious application error. An
exception was caught from the delegate
of NSFetchedResultsController during a
call to -controllerDidChangeContent:.
* -[NSMutableArray objectAtIndex:]: index 0 beyond bounds for empty array
with userInfo (null)

At the top of - (void)controller:(NSFetchedResultsController *)controller didChangeObject: I was fetching the entity out of the fetchedResultsController. Well, that really messes up things when you have just deleted the object and need to update the table. I moved the entity fetch into the NSFetchedResultsChangeUpdate portion. This then kept me from crashing out.

Related

iPhone iOS how to delete local files referenced from Core Data entity nested relationships when the entity is deleted?

I have a core data model defined as follows:
A User has many events. Each event can have many pictures. The relationships have "cascade" delete rule.
I'm trying to understand how to delete local files when the entity disappears. Is there some kind of dealloc or "finalize" method that a core data entity calls before it is gone?
Each picture entity has a reference to a local file that's stored in the app's documents directory.
When the user is deleted through table view's commitEditingStyle, I can delete the images by stepping through the relationships and delete the files manually:
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
{
// Delete the managed object for the given index path
NSManagedObjectContext *context = [[[RKObjectManager sharedManager] objectStore] managedObjectContext];
AppUser* managedObject = [self.fetchedResultsController objectAtIndexPath:indexPath];
//clean up local files before deleting the object
[self deleteLocalContentForAppUser:managedObject];
//now delete the object
[context deleteObject:managedObject];
// Save the context to remember deletion
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();
}
}
}
However, when the entities are deleted through other methods, and my fetched results controller gets notified of them, the nested relationships contain no objects. The user's events set will not have any entities to step through and delete the local content. Is this the result of the "cascade" delete rule set for the relationship?
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath *)newIndexPath
{
// UITableView *tableView = self.tableView;
//
AppUser* managedObject = anObject;
switch(type) {
case NSFetchedResultsChangeInsert:
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationNone];
// [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
// [self configureCell:[tableView cellForRowAtIndexPath:newIndexPath] atIndexPath:newIndexPath];
break;
case NSFetchedResultsChangeDelete:
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationNone];
//these methods cannot find any content to delete
[managedObject deleteLocalImages];
[managedObject deleteLocalContent];
break;
case NSFetchedResultsChangeUpdate:
[self configureCell:[self.tableView cellForRowAtIndexPath:newIndexPath] atIndexPath:newIndexPath];
[managedObject updateLocalImages];
break;
case NSFetchedResultsChangeMove:
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
You can implement the prepareForDeletion method in your NSManagedObject subclass. It is invoked automatically before an object is deleted, so that you can also delete referenced files from there.

Understanding how NSFetchedResultsController works

First off, thank you very much if you find time to help me!
I have been learning about NSFetchedResultsControllerand im trying to understand it as much as i can, however, some things about it very much confuse me. This code if from a tutorial i've found online. Basically i have the simplest fetched controller set up with really fancy methods like this
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
// The fetch controller is about to start sending change notifications, so prepare the table view for updates.
[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 NSFetchedResultsChangeInsert:
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeUpdate:
[self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
break;
case NSFetchedResultsChangeMove:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
// Reloading the section inserts a new row and ensures that titles are updated appropriately.
[tableView reloadSections:[NSIndexSet indexSetWithIndex:newIndexPath.section] withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
(NSFetchedResultsController *)fetchedResultsController {
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"FailedBankInfo" inManagedObjectContext:_context];
[fetchRequest setEntity:entity];
NSSortDescriptor *sort = [[NSSortDescriptor alloc]
initWithKey:#"details.closeDate" ascending:NO];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]];
[fetchRequest setFetchBatchSize:20];
NSFetchedResultsController *theFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:_context sectionNameKeyPath:nil
cacheName:#"Root"];
self.fetchedResultsController = theFetchedResultsController;
_fetchedResultsController.delegate = self;
[sort release];
[fetchRequest release];
[theFetchedResultsController release];
return _fetchedResultsController;
}
Do i need to create a new fetched controller everytime for different view controllers? For instance if i have a list of businesses in one list view. then i click on one of them and brings me to a list of employees in that ocmpany, i have to use a different fetchedViewController since i need to respecify the entity key right? See above my implementation.
With my current implementation, it works great for listing items. Let's say I have a view controller called VC. init i fully implemented NSFetchedResultsController. I have a barbutton add item and a method called addButtonPressed which when pressed modally adds a view (from bottom up). In this same method i have
Entity myEntity = [NSEntityDescription insertNNewObjectForEntityForName:#"Entity" inManagedObjectContext:context_]; Then i tell the code the go into another view. HOWEVER, when the animation is moving the new navigation controller up, the new cell already shows itself mid animation. From my understanding this these two methods
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath AND - (UITableViewCell *)tableView:(UITableView *)tableView get called in conjuction with the top two methods in the code snippet above. how can i fix it? i dont want anything showing up on the current view until i added a new Entity by going to another view. The second another object gets called into the context, the update methods must get called or something
What does this snippet of code do? Particularly the part with id < stuff >
(void)controller:(NSFetchedResultsController *)controller didChangeSection:(id NSFetchedResultsSectionInfo)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;
}
}
Edit: Holy $#$, formatting this frustrating. there should be didChangeSection:(id (LESS THAN EQUAL SIGN) NSFetchedResultsSectionInfo (GREATER THEN EQUAL SIGN)) in the code above
4 Where can i learn more about this. i.e what the individual content updating methods do and when they are called etc. Also Does the NSFetchedResultcontroller act as a single array? Meaning that if i want to have a tableview with multiple sections i need more ViewControllers? Once again THANKS!
I second Brad's comment, but I'll give you some pointers:
Basically yes, you need a new NSFetchedResultsController for every entity you want to query for. However, you don't need to use and NSFetchedResultsController at all, this is just a nice class to let you do fancy things with your tableView as you are doing in question #2.
Your problem here is that you are calling [NSEntityDescription insertNNewObjectForEntityForName:#"Entity" inManagedObjectContext:context_]; from your tableViewController and as soon as you call that your NSFetechedResultsController will get a message stating that a row was inserted, triggering the NSFetchedResultsChangeInsert path in your controller:didChangeObject:atIndexPath:forChangeType:newIndexPath: method. NSFetchedResultsController is an object that watches the data that is fetches from CoreData for any changes and then gives you the ability to do stuff as the changes happen. To fix your display issue, just move the insertNewObjectForEntityName call into your next viewController, and magically when you dismiss that viewController you'll have a new row.
The didChangeSection: delegate method of NSFetchedResultsController is informing you if a section has been added or deleted from your dataset. See my answer to #4 and this should be clear.
NSFetechedResultsController allows you to "section" the data it fetches using the sectionNameKeyPath: part of the init call. By supplying the name of a data point in your entity you can group your data into sections. So going back to #3 & #2 if you happen to insert a new entity that has a new value in the "section" keypath that you specified when initing the NSFetchedResultsController you'd get the didChangeSection: delegate called.
I recommend reading Apples documentation on NSFetchedResultsController and CoreData and probably their TableView Programming Guide. Don't forget to download and play with the sample code, it's easier to understand in most cases than the theories in the documentation.

NSFetchedResultsController – getting exception when a new section is created (UPDATE: might be when all rows in a section is deleted...)

I've got a view with a TableView and an NSFetchedResultsController.
I'm using a ASINetworkQueue (subclass of NSOperationQueue) and a subclass of ASIHTTPRequest (which in turn is a subclass of NSOperation) to download a JSON feed, parse it and insert corresponding entities into Core Data.
Therefore, in the ASIHTTPRequest subclass, I've got a second NSManagedObjectContext to keep everything threadsafe and nice.
Everything is fine, my background fetching/import fires of each 10 seconds or so, new entities are created and saved into the Core Data store. The NSNotification propagates its way to the ViewController and the NSFetchedResultsController and new rows appear in the TableView.
The problem occurs when the JSON contains an entity with a new value of the section key (lets call it "sectionID") – for example sectionID == 2 instead of sectionID == 1 (you get it?).
At this point, the NSFetchedResultsController should make the table view create a new section, but instead I'm getting an exception:
Serious application error. Exception was caught during Core Data change processing. This is usually a bug within an observer of NSManagedObjectContextObjectsDidChangeNotification. *** -[NSArray initWithObjects:count:]: attempt to insert nil object at objects[0] with userInfo (null)
Assertion failed: (_Unwind_SjLj_Resume() can't return), function _Unwind_SjLj_Resume, file /SourceCache/libunwind/libunwind-24.1/src/Unwind-sjlj.c, line 326.
Here is my code for the NSFetchedResultControllers delegate methods:
-(void)controllerWillChangeContent:(NSFetchedResultsController *)controller
{
[[self eventTable] beginUpdates];
}
-(void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {
NSIndexSet* set = [NSIndexSet indexSetWithIndex:sectionIndex];
switch (type) {
case NSFetchedResultsChangeInsert:
[[self eventTable] insertSections:set withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[[self eventTable] deleteSections:set withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
-(void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {
UITableView* tv = [self eventTable];
switch (type) {
case NSFetchedResultsChangeInsert:
[tv insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[tv deleteRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeUpdate:
[self tableView:tv configureCell:[tv cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
break;
}
}
-(void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
[[self eventTable] endUpdates];
}
Any thoughts about whats causing the exception? Thanks in advance!
UPDATE 2011-02-03
Not really sure whether the error occurs when a new section is created, or an old one is deleted. I almost think that this occurs when all the rows in a section is deleted, but by some reason the controller:didChangeSection:atIndex:forChangeType is not being called.
Anyone with experience of something similar?
UPDATE 2011-02-08
I think I solved it. The problem was that some extra conditions had to be taken into concern when determining whether to delete the row/section or not.
When using the code supplied in the Apple documentation (along with a few adjustments to make it work in my view), it runs OK.
I think I solved it. The problem was that some extra conditions had to be taken into concern when determining whether to delete the row/section or not. When using the code supplied in the Apple documentation (along with a few adjustments to make it work in my view), it runs OK.
UPDATE 2011-03-22
Basically I used the same approach as in SafeFetchedResultsController.
I don't know if this is the reason for your error but you are missing the NSFetchedResultsChangeMove case in your delegate methods.
case NSFetchedResultsChangeMove:
[changedTableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[changedTableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]withRowAnimation:UITableViewRowAnimationFade];
break;

How can I get my table view to redisplay data when objects are deleted?

I have table view which is in a view controller that inherits from UITableViewController.
I use a NSFetchedResultsController that I use to fetch the table data from my core data store. I've tried it with both caching on and off.
I set the delegate of the NSFetchedResultsController to be self and I've implemented controllerDidChangeContent in my view controller.
I've always implemented the table view delegate functions including commitEditingStyle which is called when the user deletes a row.
Here is what happens. The user swipes and deletes a row and commitEditingStyle is called as expected. In that function, I modify my core data objects to so that they are effectively deleted from results. In other words, if you ran the query I passed NSFetchedResultsController again, that row would now no longer be in the results set.
This alone is not enough, so I added a called to tableView reloadData at the end of commitEditingStyle. Still no luck, so I added a refresh button that calls reloadData and that doesn't help either. Going to another view controller (by hitting "back") and returning to the page doesn't usually work, but eventually it figures it out and the row will disappear correctly. Rerunning the program from scratch always works, of course, but how can I get my tableView to update correctly? controllerDidChangeContent never gets called by the way. I thought reloadData would be enough, but it doesn't seem to be.
Have you implemented the Fetched results controller's controller didChangeObject: delegate method?
The easiest way is to create a new project using navigation based template with core-data on and compare the RootViewController with your code. RootViewController already contains the code to perform the update after tableview modification.
It is something like this:
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the managed object for the given index path
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. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button.
*/
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
}
}
Then use this method to update the table view
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath *)newIndexPath {
UITableView *tableView = self.tableView;
switch(type) {
case NSFetchedResultsChangeInsert:
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeUpdate:
[self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
break;
case NSFetchedResultsChangeMove:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]withRowAnimation:UITableViewRowAnimationFade];
break;
}

How can I make deleteRowsAtIndexPaths: work with GenericTableViewController?

I'm using Matt Gallagher's GenericTableViewController idea for controlling my UITableViews. My datasource is a NSFetchedResultsController.
http://cocoawithlove.com/2008/12/heterogeneous-cells-in.html
Everything is working fine, until I try to delete a cell.
I have the following code in my View Controller:
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the managed object.
NSManagedObjectContext *context = [wineryController managedObjectContext];
[context deleteObject:[wineryController objectAtIndexPath:indexPath]];
NSError *error;
if (![context save:&error]) {
// Handle the error.
}
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
}
The final line crashes with the rather verbose explanation in the console:
*** 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 (5) must be equal to the number
of rows contained in that section before the update (5), plus or minus the number
of rows inserted or deleted from that section (0 inserted, 1 deleted).'
OK, I understand what it is saying... a row is not getting deleted (I would assume) because I'm not forwarding some message to the right place (since I have moved some code from its 'normal' location)... anyone have any idea which one? I am totally stumped on this one.
Well, bah. I just found this answer, which is not the same, but got me headed in the right direction. I'll leave this here for anyone in the future having similar troubles.
The key is to wrap the deleteRowsAtIndexPaths with begin and end tags, and force the model to update within the same block, resulting in:
[tableView beginUpdates];
[self constructTableGroups];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationFade];
[tableView endUpdates];
This caused the issue to go away, and the animations to work just perfectly.