How to implement NSFetchedResultsController delegate when multiple row is on deletion? - ios

Two items is deleted.
func controller(controller: NSFetchedResultsController!, didChangeObject anObject: AnyObject!, atIndexPath indexPath: NSIndexPath!, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath!) {
switch(type) {
case .Delete:
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
default:
break
}
}
At deletion line I get this error:
CoreData: error: Serious application error. Exception was caught
during Core Data change processing. This is usually a bug within an
observer of NSManagedObjectContextObjectsDidChangeNotification.
Invalid update: invalid number of rows in section 0. The number of
rows contained in an existing section after the update (0) must be
equal to the number of rows contained in that section before the
update (2), 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)
*** 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 (0) must be equal to the number of
rows contained in that section before the update (2), 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 need to 'batch' the updates by also implementing the controllerWillChangeContent and controllerDidChangeContent delegate methods as follows (although sorry these are Obj-c):
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
[self.tableView beginUpdates];
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
[self.tableView endUpdates];
}

Related

I tried to update after adding data to the list first, but an error occurs

DispatchQueue.main.async {
let data = try?JSONSerialization.data(withJSONObject: jsonDict["records"] as Any, options: [])
if let noticeData = try? decoder.decode([NoticeModel].self, from: data!){
for noticeAdd in noticeData {
self.noticeLists.append(Notice(title: noticeAdd.title, date: noticeAdd.createdAt, description: noticeAdd.content, isOpened: false))
self.noticeTableView.beginUpdates()
self.noticeTableView.insertRows(at: [IndexPath.init(row: self.noticeLists.count-1, section: 0)], with: .automatic)
self.noticeTableView.endUpdates()
}
}
self.chkList()
}
I tried to update after adding data but not working
'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).'
I keep getting this error only. I don't know what is causing this error.
Can you help me?

Attempt to insert rows at specific section Swift

I have tableview and after I get new data I want to update a specific section. So I update the corresponding models. My initial value of rows in that section was 3, now it suppose to be 5. I did following:
self?.addOperations(items, completion: {(pathes) in
DispatchQueue.main.async {
print("output pathes - \(pathes)")
for (index, item) in (self?.arrValues.enumerated())!{
print("operations count - \(item.operations.count), section - \(index)")
}
self?.tableView.beginUpdates()
self?.tableView.insertRows(at: pathes, with: UITableViewRowAnimation.bottom)
self?.tableView.endUpdates()
}
})
Add operation is function that loads new data, adds it to arrValues (array that contain models of cell data), and then in completion block pass array of IndexPathObject.
That code crashes my app. In the log I can see:
output pathes - [[1, 1]]
operations count - 7, section - 0
operations count - 5, section - 1
In my second section I have 5 values for now. And output IndexPath says that I have 2 rows in section (1). But the error says:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 1. 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 (3), plus or minus the number of rows inserted or deleted from that section (1 inserted, 0 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).'
It all should work. Why doesn't it and how to fix it?
My delegate methods declared like this:
func numberOfSections(in tableView: UITableView) -> Int {
return arrValues.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return arrValues[section].operations.count
}

Invalid update: invalid number of sections

Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of sections. The number of sections contained in the table view after the update (3) must be equal to the number of sections contained in the table view before the update (3), plus or minus the number of sections inserted or deleted (1 inserted, 0 deleted).'
but i insert 1 and deleted one based on data source what i missed
self.states?.append(sortedStates) //Update state property
if (self.states?.count)! > 3 {
self.states?.removeFirst()
}
self.newsFeedTableView.beginUpdates()
self.newsFeedTableView.insertSections([(self.states?.count)! - 1], with: .none)
if (self.states?.count)! > 3 {
let statesForoldestStateTime = self.states?.first
self.newestStateTime = statesForoldestStateTime?.first?.createdAt
let indexpostion = (self.states?.count)! - 3
self.newsFeedTableView.deleteSections([indexpostion], with: UITableViewRowAnimation.none)
}
self.newsFeedTableView.endUpdates()
The error says it all. When if (self.states?.count)! > 3 is false. The only section would be inserted and not deleted.
You should update your data source accordingly. The number of sections method must return someArray.count. When you insert some section, make sure to update that some array, and when you delete some section, delete the element from some array. That will resolve the issue.

Deleting the last section of a UITableView causes an exception

Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'UITableView internal bug: unable to generate a new section map with old section count: 1 and new section count: 0'
Below is the code
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
#try {
NSMutableArray *arrData = [dictStartDate* objectForKey:self.navigationItem.title];
NSLog(#"Title : %#", self.navigationItem.title);
NSLog(#"Array count : %i", (int)arrData.count);
return arrData.count;
}
#catch (NSException *ex) {
[[ProjectHandler sharedHandler]LogException:ex.name description:ex.description className:#"TodayTasksViewController" functionName:#"numberOfRowsInSection"];
return 0;
}
}
I had this problem and I solved it testing if I am deleting the last row of a section, and if so instead of deleting the row, I deleted the section. I am returning 0 to the numberOfSectionsInTableView and there is no problem, the tableView is just empty.
Bellow follows the swift code I used to check if it is the last row of a section or not and the key part, which is to delete the section and not the row.
Suppose you have objects for sections in a table view like this:
var objects: [[Object]]?
Then if you are removing a row of this table with this objects you should do:
objects![indexPath.section].removeAtIndex(indexPath.row)
if (objects![indexPath.section].count == 0) {
objects!.removeAtIndex(indexPath.section)
tableView.deleteSections(NSIndexSet.init(index: indexPath.section), withRowAnimation: .Left)
return //End the func and not execute the next step
}
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Left)
Also you could use something like this to identify if it's the last row in a section:
tableView.numberOfRowsInSection(indexPath.section)
You seem to be deleting the only section in the table. A table must have at least 1 section. You might want to check your code to check that you are not returning 0 in numberOfSectionsInTableView:

Unexpected inserts sent to NSFetchedResultsController

I have a model with Managers and Employees. Managers have a relationship to employees. I have a UITableViewController utilizing FRC. The FRC is initialized over the Manager model with a sort descriptor on the name.
The normal things work - adding managers and deleting managers work as expected. However, what's strange is that when an employee of an existing manager is updated (changing some coredata property on employee), FRC sends an insert on the manager of the employee being updated. CoreData obviously gets confused, and reports an assertion:
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 (8) must be equal to the number of rows contained in
that section before the update (8), plus or minus the number of rows
inserted or deleted from that section (2 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)
The strange thing is that in the assertion, it seems to think that 2 inserts were performed with 1 delete. I logged the didChangeObject and I only see a single insert.
Why is an insert being sent for the Manager when it's employee is updated?
Update - showing the code
Here's my view controller's initialization of FRC. Note that I'm modeling companies, so managers belong to companies (which explains the predicate:
let fetchRequest = NSFetchRequest(entityName: "ManagerModel")
fetchRequest.predicate = NSPredicate(format: "%K == %#", "company", self.currentCompany)
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "name", ascending: false)]
Here's my view controller's didChangeObject function:
func controller(controller: NSFetchedResultsController,
didChangeObject anObject: NSManagedObject,
atIndexPath indexPath: NSIndexPath?,
forChangeType type: NSFetchedResultsChangeType,
newIndexPath: NSIndexPath?)
{
let manager = anObject as! ManagerModel
switch(type)
{
case NSFetchedResultsChangeType.Insert:
log.info("Inserting row at index path \(newIndexPath!), \(manager.name)")
break
case NSFetchedResultsChangeType.Delete:
log.info("Deleting row at index path \(indexPath!), \(manager.name)")
break
case NSFetchedResultsChangeType.Update:
log.info("Updating row at index path \(indexPath!), \(manager.name)")
break
case NSFetchedResultsChangeType.Move:
log.info("Moving row at index path \(indexPath!) to \(newIndexPath!), \(manager.name)")
break
}
}
My code syncs data and updates data. Here is the code that updates the employee by first searching for it.
let cdEmp = cdMgr.findEmpoyeeById(mailId: downloadedEmp.empId)
if (cdEmp != nil)
{
// If I don't update the cdEmp model, I have no problems.
// If I update any of the cdEmp properties, I'm getting
// the insert notification on the manager instance cdMgr
cdEmp!.name = downloadedEmp.name
}

Resources