Search code examples
iosswiftnsfetchedresultscontroller

NSFetchedResultsController inserts the same cell into two sections only when controller is about to insert another section


This is my Message.swift file:

@objc(Message)
class Message: NSManagedObject {

    @NSManaged var content: String
    @NSManaged var createdAt: NSDate
    @NSManaged var identifier: Int64

    @NSManaged var conversation: Conversation

    @NSManaged var sender: Contributor

    var normalizedCreatedAt: NSDate {
        return createdAt.dateWithDayMonthAndYearComponents()!
    }
}

This is how I setup my FRC:

private func setupFetchedResultsController() {

    let context = NSManagedObjectContext.MR_defaultContext()
    let fetchRequest = NSFetchRequest(entityName: "Message")
    let createdAtDescriptor = NSSortDescriptor(key: "createdAt", ascending: true)

    fetchRequest.predicate = NSPredicate(format: "conversation.identifier = %lld", conversation.identifier)
    fetchRequest.sortDescriptors = [createdAtDescriptor]

    fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: context, sectionNameKeyPath: "normalizedCreatedAt", cacheName: nil)
    fetchedResultsController.delegate = self

    try! fetchedResultsController.performFetch()
    tableView.reloadData()
}

with its standard delegate.

On viewDidLoad my controller has 1 section with 1 row. I print it on console using the following function:

func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

    print("--->>>")
    print(section)
    print(fetchedResultsController.sections![section].objects!.count)
    return fetchedResultsController.sections![section].objects!.count
}

func numberOfSectionsInTableView(tableView: UITableView) -> Int {
    return fetchedResultsController?.sections?.count ?? 0
}

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

    let message = fetchedResultsController?.objectAtIndexPath(indexPath) as! Message
    let cellIdentifier = message.sender.identifier == Settings.currentUser?.profile?.identifier ? SentTableViewCellIdentifier : ReceivedTableViewCellIdentifier
    let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! TableViewCell

    cell.cellTitleLabel?.text = message.content

    return cell
}

and the output is following:

--->>>
0
1

Once I try to add just ONE another message with different section, the following I get:

--->>>
0
2
--->>>
1
1

and then the 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 (2) 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, 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)

Why it happens like that?

NSFetchedResultsController for some reason loads the same cell into two sections: first and second. Why?

NOTE:

  • The problem arise ONLY when FRC insert new section. If it needs to insert row into existing section, there is no problem. Issue is strong related to sections.
  • The problem is ONLY when FRC try to insert a SECOND section. When it is about third or fourth section, there is no problem at all.

Solution

  • I do not know why, but to make it working you need to replace:

    fetchedResultsController.sections![section].objects!.count
    

    with

    fetchedResultsController.sections![section].numberOfObjects
    

    For some reason objects!.count returns incorrect number of objects opposite to numberOfObjects property.