title: NSFetchResultController not updating cell for CollectionView
body: <p>I'm trying to use NSFetchController with a CollectionView. My problem is when I change a property value for my NSManagedObject, my cells are not updating correctly. Here is a <a href="https://youtu.be/OSK98GthllM" rel="nofollow noreferrer">link</a> to better show what I am talking about. Okay, so if you watch the video you can see basically what the app does. It has a bunch of hero's and when you click a cell it goes to another view controller and shows a picture, name and publisher. Now you were also able to see the problem, when I changed the name too 'Amanda Doe' and saved it. It was inserted in the right place at the beginning, but the cell label wasn't updated.</p>
  
  <p>When I click the cell again it shows Amanda Doe on the navigation title, but the cell label is still the same. In order for the name to show I have to restart the app or click the same cell and just click save again.</p>
  
  <p>This is how my NSFetchResultController looks like. I sort by the Publisher name then by the Hero name. It also has multiple sections based on the publisher.</p>
  
  <pre><code>private var blockOperations: [BlockOperation] = []
  
  lazy var heroController: NSFetchedResultsController&lt;Hero&gt; = {
      let request: NSFetchRequest&lt;Hero&gt; = Hero.fetchRequest()
      let nameSort = NSSortDescriptor(key: "name", ascending: true)
      let publisherSort = NSSortDescriptor(key: "publisher.name", ascending: true)
      request.sortDescriptors = [publisherSort, nameSort]
  
      let context = CoreDataManager.shared.persistentContainer.viewContext
      let controller = NSFetchedResultsController(fetchRequest: request, managedObjectContext: context, sectionNameKeyPath: "publisher.name", cacheName: nil)
      controller.delegate = self
  
      do {
          try controller.performFetch()
      } catch let err {
          print("Unable to fetch heros from controller.", err)
      }
  
      return controller
  }()
  </code></pre>
  
  <p>This code here is basically the boilerplate code that I found that most people use to get the NSFetchResultController to work with the CollectionView.</p>
  
  <pre><code>func controllerWillChangeContent(_ controller: NSFetchedResultsController&lt;NSFetchRequestResult&gt;) {
      blockOperations.removeAll(keepingCapacity: false)
  }
  
  func controller(_ controller: NSFetchedResultsController&lt;NSFetchRequestResult&gt;, didChange sectionInfo: NSFetchedResultsSectionInfo, atSectionIndex sectionIndex: Int, for type: NSFetchedResultsChangeType) {
  
      let op: BlockOperation
  
      switch type {
      case .delete:
          op = BlockOperation { self.collectionView.deleteSections(IndexSet(integer: sectionIndex))}
          blockOperations.append(op)
      case .insert:
          op = BlockOperation { self.collectionView.insertSections(IndexSet(integer: sectionIndex))}
          blockOperations.append(op)
      case .move:
          break
      case .update:
          op = BlockOperation { self.collectionView.reloadSections(IndexSet(integer: sectionIndex))}
          blockOperations.append(op)
      @unknown default:
          fatalError()
      }
  }
  
  func controller(_ controller: NSFetchedResultsController&lt;NSFetchRequestResult&gt;, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType,newIndexPath: IndexPath?) {
  
      let op: BlockOperation
      switch type {
      case .insert:
          guard let newIndexPath = newIndexPath else { return }
          op = BlockOperation { self.collectionView.insertItems(at: [newIndexPath]) }
      case .delete:
          guard let indexPath = indexPath else { return }
          op = BlockOperation { self.collectionView.deleteItems(at: [indexPath]) }
      case .move:
          guard let indexPath = indexPath,  let newIndexPath = newIndexPath else { return }
          op = BlockOperation { self.collectionView.moveItem(at: indexPath, to: newIndexPath) }
      case .update:
          guard let indexPath = indexPath else { return }
          op = BlockOperation { self.collectionView.reloadItems(at: [indexPath]) }
      @unknown default:
          fatalError()
      }
  
      blockOperations.append(op)
  }
  
  func controllerDidChangeContent(_ controller: NSFetchedResultsController&lt;NSFetchRequestResult&gt;) {
      collectionView.performBatchUpdates({
          self.blockOperations.forEach { $0.start() }
      }, completion: { finished in
          self.blockOperations.removeAll(keepingCapacity: false)
      })
  }
  </code></pre>
  
  <p>So am I missing something, is it because I am missing .move case on the section? I was playing around with the app and found out if the cell doesn't move or have to move to a new index path it works fine. Why is that? Would really appreciate any help. Thanks.</p>
  
  <p><strong>EDIT 1</strong> </p>
  
  <p>After playing around with the code some more, I came across another problem. Problem 1 is as you seen in the video. Cell is not getting updated correctly and I don't know why. I tried to debug it as @pbasdf suggested and found out this. First when I change the name to 'Amanda Doe' it triggers the <em>didChangeAnObject: .move</em> case twice but never the <em>didChangeAnObject: .update</em>. When I click on the same cell again and change the name to 'Amanda Doee' it calls <em>didChangeAnObject: .update</em> and updates the cell with the correct name. So basically if the <em>didChangeAnObject: .move</em> case is called the cell doesn't update and if the <em>didChangeAnObject: .update</em> case is called it updates the cell with the correct name.</p>
  
  <p>Problem 2 is when I change the publisher name. So for now the name is 'Amanda Doee' and publisher name 'ABC Studios'. Now if I just change the publisher name to 'ABC Studioss' it should move the cell to a new section called 'ABC Studioss' because of my NSFetchResultController. The thing is it doesn't do that it stays in the same position and it calls the <em>didChangeAnObject: .update</em> case. </p>
  
  <p>Now I found a way that it works. In order for it to work I need to change the Name &amp; Publisher. It won't work if I just change the publisher. So I would have to change 'Amanda Doee' to 'Amanda Doeee' and 'ABC Studios' to 'ABC Studioss'. This is how the methods get called when I change the name and publisher and save it. <em>didChangeSectionInfo: .insert</em>, <em>didChangeAnObject: .move</em>, <em>didChangeSectionInfo: .insert</em>, <em>didChangeAnObject: .move</em>. Now if I was to just change the name of the publisher this only gets called <em>didChangeAnObject: .update</em>. I would have to relaunch my app to see the changes with a new section called 'ABC Studioss' with 'Amanda Doee'.</p>
  
  <p>So I really don't know what is going on &amp; why it is behaving like this.</p>
  
  <p><strong>Edit 2</strong></p>
  
  <p>Here is the code I use in the other VC to save edits or create a new hero.</p>
  
  <pre><code>@objc private func handleSave() {
      let context = CoreDataManager.shared.persistentContainer.viewContext
      if let hero = hero {
  
          hero.name = nameTF.text ?? ""
          hero.publisher?.name = publisherTF.text
  
          do {
              try context.save()
          }
          catch let err {
              print("Unable to save hero edits.", err)
          }
      }
      else {
          print("Create new hero.")
  
          let hero = Hero(context: context)
          hero.name = nameTF.text
          let publisher = Publisher(context: context)
          publisher.name = publisherTF.text
          hero.publisher = publisher
  
          guard let imageData = heroImageView.image?.jpegData(compressionQuality: 1) else {
              try? context.save()
              return
          }
  
          let photo = Photo(context: context)
          photo.data = imageData
          hero.photo = photo
  
          try? context.save()
      }
      navigationController?.popViewController(animated: true)
  }
  </code></pre>