`UICollectionView` documentation is sparse at best. `performBatchUpdates`is a complete enigma. I have been trying to get all 4 (move, add, delete, reload) operations working correctly in the update space. I have some hope that Apple has correctly implemented the handling but I wish they would explain what on earth they are doing.Here is what I'm trying to get to work. I have two arrays with similar items in various orders and animate the changes. Simple right!? Here are the arrays: po newItems.map({ $0.representedID }) ▿ 3 elements - 0 : "5ccbdfd08d9b423a9bb78310ffe7e92b" - 1 : "9130a12b035543099f30a6cd9c6b6081" - 2 : "e0d1a52ed9f3440b859d47db4f4709f4" po previous.map({ $0.representedID }) ▿ 4 elements - 0 : "9130a12b035543099f30a6cd9c6b6081" - 1 : "458b0523c4104a4bbcb420dc01aeb143" - 2 : "e0d1a52ed9f3440b859d47db4f4709f4" - 3 : "5ccbdfd08d9b423a9bb78310ffe7e92b"You can see we lose one item between previous and new items and the rest shift around a bit.I have some logic to calculate the differences and process that in the perform batch updates which are below: Changes for section:0 Removing :[1] Moving from:0 to 1 Moving from:3 to 0 reloading :[2]This makes sense to make the previous turn into the new we remove 1, move item in 0 to 1, move item in 3 to zero and simply reload index 2.This crashes every time here: *** Assertion failure in -[UICollectionView _endItemAnimationsWithInvalidationContext:tentativelyForReordering:animator:], /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKit_Sim/UIKit-3683.300.2/UICollectionView.m:5744 *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'attempt to perform an insert and a move to the same index path (<NSIndexPath: 0xc000000000200016> {length = 2, path = 0 - 1})'Where is this magic insert coming from? Any ideas the algorithm they are using performing these changes?Here is the sequence of the batch changes: collectionView.performBatchUpdates({ model.applyChanges(changes) print("Changes for:\(section)") if !changes.removeIndexes.isEmpty { print("Removing :\([Int](changes.removeIndexes))") self.collectionView.deleteItems(at: changes.removeIndexes.map({ IndexPath(row: $0, section: section)})) } for (from, to) in zip(changes.moveFrom, changes.moveTo).sorted(by: { $0.0 < $1.0 }) { print("Moving from:\(from) to \(to)") self.collectionView.moveItem(at: IndexPath(row: from, section: section), to: IndexPath(row: to, section: section)) } if !changes.addIndexes.isEmpty { print("adding :\([Int](changes.addIndexes))") self.collectionView.insertItems(at: changes.addIndexes.map({ IndexPath(row: $0, section: section)})) } if !changes.reloadIndexes.isEmpty { print("reloading :\([Int](changes.reloadIndexes))") self.collectionView.reloadItems(at: changes.reloadIndexes.map({ IndexPath(row: $0, section: section)})) } }, completion: { _ in print("Done updating:\(section)") self.updateEmptyState() })