Unexpected crash in CollectionView iOS

1k Views Asked by At

I found a crashed on crashlytics that comes from users but is not reproducible at my end.

The projects uses collectionviews at multiple places and I have even verified the cell class being set correctly in storyboard.

Note: The Code uses RxSwift.

Any help would be greatly appreciated.

Fatal Exception: NSInternalInconsistencyException
the cell returned from -collectionView:cellForItemAtIndexPath: does not have a reuseIdentifier - cells must be retrieved by calling -dequeueReusableCellWithReuseIdentifier:forIndexPath:


Fatal Exception: NSInternalInconsistencyException
0  CoreFoundation                 0x1ae60b298 __exceptionPreprocess
1  libobjc.A.dylib                0x1c2365480 objc_exception_throw
2  CoreFoundation                 0x1ae518cc8 -[CFPrefsSearchListSource addManagedSourceForIdentifier:user:]
3  Foundation                     0x1af850128 -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:]
4  UIKitCore                      0x1b062c7c0 -[UICollectionView _createPreparedCellForItemAtIndexPath:withLayoutAttributes:applyAttributes:isFocused:notify:]
5  UIKitCore                      0x1b062dce8 -[UICollectionView _prefetchItemsForPrefetchingContext:maxItemsToPrefetch:]
6  UIKitCore                      0x1b0635634 -[UICollectionView layoutSubviews]
7  UIKitCore                      0x1b13ae6d4 -[UIView(CALayerDelegate) layoutSublayersOfLayer:]
8  QuartzCore                     0x1b1824424 -[CALayer layoutSublayers]
9  QuartzCore                     0x1b182abac CA::Layer::layout_if_needed(CA::Transaction*)
10 QuartzCore                     0x1b183616c CA::Layer::layout_and_display_if_needed(CA::Transaction*)
11 QuartzCore                     0x1b177e578 CA::Context::commit_transaction(CA::Transaction*, double, double*)
12 QuartzCore                     0x1b17a92c8 CA::Transaction::commit()
13 QuartzCore                     0x1b17aa530 CA::Transaction::observer_callback(__CFRunLoopObserver*, unsigned long, void*)
14 CoreFoundation                 0x1ae589588 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__
15 CoreFoundation                 0x1ae583bb8 __CFRunLoopDoObservers
16 CoreFoundation                 0x1ae584154 __CFRunLoopRun
17 CoreFoundation                 0x1ae583818 CFRunLoopRunSpecific
18 GraphicsServices               0x1c4c89570 GSEventRunModal
19 UIKitCore                      0x1b0eaf0e8 -[UIApplication _run]
20 UIKitCore                      0x1b0eb4664 UIApplicationMain

The code for cellForItemAt

 func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: MyCollectionViewCell.identifier, for: indexPath) as? MyCollectionViewCell else{return UICollectionViewCell()}
        cell.config(collectionView.tag == 1 ? dataSourceFirst[indexPath.row] : dataSourceSecond[indexPath.row])
        return cell
    }
1

There are 1 best solutions below

1
Anton Breza On

For anyone facing the same issue: return UICollectionViewCell() is causing the crash.

Don't mix UICollectionViewCell with UITableViewCell, which is safe to return after initialization.

The fix is pretty straightforward:

  1. During collection view setup register a cell type for reuse identifier collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "Cell")
  2. Insead of init (return UICollectionViewCell()) just dequeue the very same cell type for the same identifier. collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath)

In my case looks like the below:

    func setupCollectionView() {
       ...
       collectionView.registerCell(EmptyCell.self)
       ...
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        guard let controller = controller else {
            //instead of return UICollectionViewCell()
            return collectionView.dequeueCell(EmptyCell.self, indexPath: indexPath).setup(.songDetails)
        }
        let cell = collectionView.dequeueCell(SectionSongMenuCell.self, indexPath: indexPath)
        ...
        return cell
   }

   ***

   extension UICollectionView {

      func registerCell<T: UICollectionViewCell>( _ type: T.Type) {
          self.register(type.nib, forCellWithReuseIdentifier: type.identifier)
      }

      func dequeueCell<T: UICollectionViewCell>(_ type: T.Type, indexPath: IndexPath) -> T {
        return self.dequeueReusableCell(
            withReuseIdentifier: type.identifier,
            for: indexPath) as! T
      }
  }