Crash in iOS18

I have a UITableView which contains a UICollectionView in the first row. It used to work fine in iOS17, but now I get a crash when running with Xcode 16 / iOS18 beta:

Expected dequeued view to be returned to the collection view in preparation for display. When the collection view's data source is asked to provide a view for a given index path, ensure that a single view is dequeued and returned to the collection view. Avoid dequeuing views without a request from the collection view. For retrieving an existing view in the collection view, use -[UICollectionView cellForItemAtIndexPath:] or -[UICollectionView supplementaryViewForElementKind:atIndexPath:]

This is my UITableView delegate call:

    AddEditDataCell *cell = nil;
    if (indexPath.section == 0) {
        
        if (indexPath.row == 0) {
            AddEditDataContactsCell *contactNameCell = (AddEditDataContactsCell *)[self cellForContactNamesCollectionAtIndexPath:indexPath tableView:tableView];
            return contactNameCell; 


- (AddEditDataContactsCell *)cellForContactNamesCollectionAtIndexPath:(NSIndexPath *)indexPath tableView:(UITableView *)tableView {
    AddEditDataContactsCell *contactsCell = (AddEditDataContactsCell *)[self.tableView dequeueReusableCellWithIdentifier:@"ContactsCell" forIndexPath:indexPath];
    if (self.collectionNameCell == nil) {
        self.collectionNameCell = [contactsCell.collectionView dequeueReusableCellWithReuseIdentifier:@"LogContactNameCollectionCellIdentifier" forIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];
        contactsCell.nameCellDelegate = self;
    }

    contactsCell.frame = CGRectZero;
    [contactsCell setNeedsLayout];
    [contactsCell.collectionView reloadData];
    
    contactsCell.collectionViewHeightConstraint.constant = contactsCell.collectionView.collectionViewLayout.collectionViewContentSize.height;
    
    [contactsCell.collectionView.collectionViewLayout invalidateLayout];
    return contactsCell;
}

I'm getting a crash for decoration views somewhere inside UIKitCore which is definitely out of my control – the code doesn't dequeue or care about instantiation of decoration views at all – all it does is inserting the registered layout attributes in the flow layout subclass, the rest of it (instantiation of the registered view & queuing/dequeuing) is provided by UIKit.

I have a ton of crashes starting with iOS 18, not a single one on iOS 17 and prior. Call stack:

Thread 1: "*** -[__NSArrayM objectAtIndex:]: index 4 beyond bounds [0 .. 3]"

#1	0x000000018039c67c in -[__NSArrayM objectAtIndex:] ()
#2	0x0000000185a9aa54 in -[_UICollectionViewSubviewManager dequeueReusableViewWithReuseIdentifier:elementKind:elementCategory:] ()
#3	0x00000001851bc3cc in -[UICollectionView _dequeueReusableViewOfKind:withIdentifier:forIndexPath:viewCategory:] ()
#4	0x00000001851a6ab4 in -[UICollectionView _createPreparedSupplementaryViewForElementOfKind:atIndexPath:layout:withLayoutAttributes:applyAttributes:] ()
#5	0x00000001851af69c in -[UICollectionView _createVisibleViewsForSingleCategoryAttributes:limitCreation:fadeForBoundsChange:] ()
#6	0x00000001851af824 in -[UICollectionView _createVisibleViewsForAttributes:fadeForBoundsChange:notifyLayoutForVisibleCellsPass:] ()
#7	0x00000001851ad9c4 in -[UICollectionView _updateVisibleCellsNow:] ()

Array index out of bounds. Definitely not an array in reach, very internal operations. This is also preceded by the following warning thrown into Console:

UICollectionView internal inconsistency: attempted to queue view that is already in the reuse queue. Collection view: <TripItineraryDayCollectionView: 0x146917400; baseClass = UICollectionView; frame = (0 0; 402 682); clipsToBounds = YES; autoresize = W+H; gestureRecognizers = <NSArray: 0x600000e5d770>; backgroundColor = UIExtendedGrayColorSpace 0 0; layer = <CALayer: 0x600000393440>; contentOffset: {0, 0}; contentSize: {402, 662}; adjustedContentInset: {0, 0, 0, 0}; layout: <TripItineraryDayViewLayout: 0x1463d6770>; dataSource: <TripItineraryDayView: 0x1463d6360; frame = (0 102; 402 682); clipsToBounds = YES; autoresize = W+H; backgroundColor = UIExtendedGrayColorSpace 0 0; layer = <CALayer: 0x6000003974a0>>>; view: <TransportLineDecorationView: 0x1059e51a0; baseClass = UICollectionReusableView; frame = (14 257; 54 69); userInteractionEnabled = NO; layer = <CALayer: 0x6000004180c0>>; layout attributes: <TransportDecorationViewLayoutAttributes: 0x1463e34a0; index path: (2-4); element kind: (TripItineraryDayTransportDecorationView); frame = (14 257; 54 69)>

Disabling the decoration views makes the crash go away.

Something has probably changed in iOS 18 which might have sense for cells (where providing the views in the delegate properly is mandatory) or supplementary views, yet it doesn't work really well with decoration views. It gives me the impression that supplementary and decoration views have been merged into one queue stack internally, but something's not right.

I had similar issue with iOS18.(issue exists in below code)

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {

    guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CustomCollectionViewCell.collectionTextCell.rawValue, for: indexPath) as? CollectionTextCell else {

        return UICollectionViewCell()

    }

    cell.isDetailPage = isFromDetailPage

    cell.manageFocus(isFocusSelected: isSelectedFocus == indexPath.row)

    switch detailComponentType[indexPath.row] {

    case .episodes, .trailer, .morelikethis, .castcrew, .OtherChannelsLive, .recommendeLivechannelsforyou, .similarContentLive, .highlightsSports, .otherTournaments, .readMore, .playlist:

        cell.manageData(data:  detailComponentType[indexPath.row])

    case .bestOfProvider: cell.textlbl.text = Constant.bestOfProvider + /content_Data?.where_to_watch?.first?.provider?.name

    case .otherSportsType: cell.textlbl.text = Constant.otherSportsType + /content_Data?.format + " " + Constant.tournaments

    }

    return cell

}

I am getting the same crash but my use case shouldn't be triggering the crash by Apple's description.

In my case, I have a custom function to dynamically calculate cell sizes defined as:

public func intrinsicCellSize(
        forCellAtIndexPath indexPath: IndexPath,
        width: CGFloat? = nil,
        height: CGFloat? = nil
    ) -> CGSize {
        guard let dataSource = dataSource else {
            return .zero
        }
        let cell = dataSource.collectionView(self, cellForItemAt: indexPath)
        
        if let cell = cell as? HeightProvidingCell, let width {
            return .init(width: width, height: cell.provideHeight(for: width))
        }

        let targetSize = CGSize(
            width: width ?? UIView.layoutFittingExpandedSize.width,
            height: height ?? UIView.layoutFittingExpandedSize.height
        )
        let size = cell.contentView.systemLayoutSizeFitting(
            targetSize,
            withHorizontalFittingPriority: .fittingSizeLevel,
            verticalFittingPriority: .fittingSizeLevel
        )
        return size
    }

I call this in the sizeForItemAt delegate function. This was working flawlessly before updating macOS and Xcode but now this crashes. Specifically, it crashes with the call

let cell = dataSource.collectionView(self, cellForItemAt: indexPath)

By Apple's error message, this should be fine as I'm not calling deque, the error message even suggests that you use this method.

Perhaps the issue is the order in which UIKit calls the delegate and dataSource methods. By adding print statements, I discovered that sizeForItemAt is called before cellForItemAt so in my case, the flow of function calls is:

  • sizeForItemAt (Called by UIKit)
  • cellForItemAt (Called by me)
  • cellForItemAt (Called by UIKit)

Perhaps UIKit is expecting the first call of cellForItemAt to be made by UIKit and not the user. If this is the case, my app is broken since I relied heavily on this

I just want to provide a solution that I've found for our use case. We had the following:

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(
            withReuseIdentifier: someCellId,
            for: indexPath
        ) as! SomeCollectionViewCell

        // some logic
        return cell

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        let cell = collectionView.dequeueReusableCell(
            withReuseIdentifier: someCellId,
            for: indexPath
        ) as! SomeCollectionViewCell

        // some more logic
}

I have changed the above code to the below, which fixed the crash.

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(
            withReuseIdentifier: someCellId,
            for: indexPath
        ) as! SomeCollectionViewCell

        // some logic
        return cell

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        let cell = collectionView.cellForItem(at: indexPath) as? SomeCollectionViewCell

        // some more logic
}

As you can see, we were using the dequeueReusableCell function twice, which caused the crash. Using it only once fixed it for us.

I also encountered the crash problem. This is the original code:

  • (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { HomeCellUnconnected *cell = [collectionView dequeueReusableCellWithReuseIdentifier:CellClassNameUnconnected forIndexPath:indexPath];

    if (self.datas.count > indexPath.row) { NSString *cellClassName = self.datas[indexPath.row]; cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellClassName forIndexPath:indexPath]; }

    return cell;

}

this is the modified code:

  • (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { NSString *cellClassName = self.datas[indexPath.row]; UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellClassName forIndexPath:indexPath];

    return cell;

}

Crash in iOS18
 
 
Q