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;
}

@zulfishah Could you create a test project and share a link to the project. I'd be happy to take a look. Creating a test project, goes over the criteria for a focused test project.

We had a similar issue. Turns out we were calling dequeReusableCellWithIdentifier: to create a cell in another method which we used for size estimates, and it seems like on ios 18 the UICollectionView really does not like that. If you deque a cell it expects you to give it that cell, eg in response to cellForItemAtIndexPath.

Changing that other cell creation to just alloc/init fixed the crash for us.

Try "self.collectionNameCell = [ .... alloc] init];" instead of deque.

I have experiences a similar issue where when running the code using Xcode 16.0 / iOS 18, the app will crash. The trigger point is where instead of dequeueReusableCell, I pull from the previously saved "self.bannerItem" and the app crash with the follow error.

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'The collection view's data source returned a cell that is in the reuse queue. Cells must be retrieved by calling -dequeueConfiguredReusableCellWithRegistration:forIndexPath:item: or -dequeueReusableCellWithReuseIdentifier:forIndexPath:. Collection view: <UICollectionView: 0x107824200; frame = (0 59; 393 759); clipsToBounds = YES; gestureRecognizers = <NSArray: 0x600000c6eca0>; backgroundColor = <UIDynamicSystemColor: 0x600001767040; name = systemBackgroundColor>; layer = <CALayer: 0x600000248c60>; contentOffset: {0, 766.33333333333337}; contentSize: {393, 2340}; adjustedContentInset: {0, 0, 0, 0}; layout: <UICollectionViewFlowLayout: 0x10690c380>; dataSource: <AppTestiOS18.ViewController: 0x10690cd20>>; index path: (0-0); cell: <AppTestiOS18.ItemCollectionViewCell: 0x105707530; baseClass = UICollectionViewCell; frame = (46.6667 1740; 300 600); hidden = YES; layer = <CALayer: 0x60000024e6a0>>'

The actual code can run using Xcode 15.4 and iOS 17 without any issues

class ItemCollectionViewCell:UICollectionViewCell {
    static let cellIdentifier = "\(type(of: ItemCollectionViewCell.self))"
}

class ViewController: UIViewController {
    
    struct ContentCellItem {
        let height:CGFloat
    }
    
    enum ContentSection {
        case TopBanner(ContentCellItem)
        case FeatureBanner(ContentCellItem)
        case Title(ContentCellItem)
        case Webconent(ContentCellItem)
        case RelatedArticle(ContentCellItem)
    }
    
    let dataSrc:[ContentSection] = {
        var v:[ContentSection] = []
        v.append(.TopBanner(.init(height: 200)))
        v.append(.FeatureBanner(.init(height: 300)))
        v.append(.Title(.init(height: 400)))
        v.append(.Webconent(.init(height: 800)))
        v.append(.RelatedArticle(.init(height: 600)))
        return v
        
    }()

    let collectionView:UICollectionView = {
        let t =  UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout())
        
        t.register(ItemCollectionViewCell.classForCoder(), forCellWithReuseIdentifier: ItemCollectionViewCell.cellIdentifier)
        return t
    }()
    
    private var bannerItem:ItemCollectionViewCell!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        
        collectionView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(collectionView)
        view.leadingAnchor.constraint(equalTo: collectionView.leadingAnchor).isActive = true
        view.trailingAnchor.constraint(equalTo: collectionView.trailingAnchor).isActive = true
        view.safeAreaLayoutGuide.topAnchor.constraint(equalTo: collectionView.topAnchor).isActive = true
        view.safeAreaLayoutGuide.bottomAnchor.constraint(equalTo: collectionView.bottomAnchor).isActive = true
        
        collectionView.layer.borderColor = UIColor.red.cgColor
        collectionView.layer.borderWidth = 3.0
        
        collectionView.delegate = self
        collectionView.dataSource = self
    }
}

extension ViewController: UICollectionViewDataSource {
    
    func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 1
    }
    
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return dataSrc.count
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        
        let section = dataSrc[indexPath.row]
        switch section {
        case .TopBanner(_):
            
            if self.bannerItem == nil {
                guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier:ItemCollectionViewCell.cellIdentifier , for: indexPath) as? ItemCollectionViewCell else {
                    return UICollectionViewCell()
                }
                self.bannerItem = cell
            }
            
            self.bannerItem.layer.borderColor = UIColor.green.cgColor
            self.bannerItem.layer.borderWidth = 1.0
            return bannerItem
            
        default:
            guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier:ItemCollectionViewCell.cellIdentifier , for: indexPath) as? ItemCollectionViewCell else {
                return UICollectionViewCell()
            }
            cell.layer.borderColor = UIColor.blue.cgColor
            cell.layer.borderWidth = 1.0
            return cell
        }
    }
    
}


extension ViewController: UICollectionViewDelegateFlowLayout {
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        
        let cellWidth:CGFloat = 300
        
        guard indexPath.row < dataSrc.count else {
            return CGSize(width: cellWidth, height: 1)
        }

        let section = dataSrc[indexPath.row]
        
        
        switch section {
        case .TopBanner(let item):
            return CGSize(width: cellWidth, height: item.height)
        case.FeatureBanner(let item):
            return CGSize(width: cellWidth, height: item.height)
        case .Title(let item):
            return CGSize(width: cellWidth, height: item.height)
        case .Webconent(let item):
            return CGSize(width: cellWidth, height: item.height)
        case .RelatedArticle(let item):
            return CGSize(width: cellWidth, height: item.height)
        }
    }
}

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: "FTShelfTagsPageCell", for: indexPath) as? FTShelfTagsPageCell else { return UICollectionViewCell() } cell.selectionBadge?.isHidden = viewState == .none ? true : false

    if indexPath.section == 0 {
        guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "FTShelfTagsBooksCell", for: indexPath) as? FTShelfTagsBooksCell else {
            return UICollectionViewCell()
        }
        return cell
    } else if indexPath.section == 1 {
        let item = pages[indexPath.row]
        cell.updateTagsItemCellContent(tagsItem: item, isRegular: self.traitCollection.isRegular)
    }
    cell.isSelected = true
    return cell
}

It is dequeuing 2 cells though I return single cell.

After simplification like below, issue got fixed. (only 1 cell ll be dequeued at a time.)

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    if indexPath.section == 0 {
        guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "FTShelfTagsBooksCell", for: indexPath) as? FTShelfTagsBooksCell else {
            return UICollectionViewCell()
        }
        return cell
    } else  {
        guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "FTShelfTagsPageCell", for: indexPath) as? FTShelfTagsPageCell else {
            return UICollectionViewCell()
        }
        let item = pages[indexPath.row]
        cell.selectionBadge?.isHidden = viewState == .none ? true : false
        cell.updateTagsItemCellContent(tagsItem: item, isRegular: self.traitCollection.isRegular)
        cell.isSelected = true
        return cell
    }
}
Crash in iOS18
 
 
Q