iosswiftwwdc

Swift, iOS15, UIKit, CollectionView header issue


I am testing iOS15 and some new functionalities of UIKit. I've encountered some issues, not sure how to solve them. I did not change that code. This is just a piece of code that worked perfectly with the iOS 14, now after updating my target, it throws an error.

Xcode crashes the moment when my custom header for the UICollectionView of type UICollectionElementKindSectionHeader is being returned for the dataSource. Here is my code:

private func configureDataSource() {
      dataSource = UICollectionViewDiffableDataSource<Section, Follower>(collectionView: collectionView, cellProvider: { (collectionView, indexPath, followers) -> UICollectionViewCell? in
         let cell = collectionView.dequeueReusableCell(withReuseIdentifier: FollowerCell.reuseId, for: indexPath) as! FollowerCell
         cell.set(on: followers)
         return cell
      })
      
      dataSource.supplementaryViewProvider = { (collectionView, kind, indexPath) in
         let header = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionView.elementKindSectionHeader,
                                                                      withReuseIdentifier: FollowersCollectionHeaderView.reuseId,
                                                                      for: indexPath) as! FollowersCollectionHeaderView
         
         header.set(with: self.user)
         return header
      }
   }

The log says:

the view returned from -collectionView:viewForSupplementaryElementOfKind:atIndexPath: does not match the element kind it is being used for. When asked for a view of element kind 'FollowersCollectionHeaderView' the data source dequeued a view registered for the element kind 'UICollectionElementKindSectionHeader'.

I did cast UICollectionElementKindSectionHeader to FollowersCollectionHeaderView, therefore I am not sure what is the issue here.

I've watched WWDC21 what's new in UIKit but haven't seen any mentioning of any change for that particular code.

Any suggestions, what to fix in that code?


Solution

  • Here is the partial solution that I came up with. Apple recommends using object's ID as a reference for the collectionView cells.

    enum Section { case main }
    
    
    var dataSource: UICollectionViewDiffableDataSource<Section, Follower.ID>!
    
    // MARK: - Collection View configurations
        fileprivate lazy var collectionView: UICollectionView = {
            let collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: UIHelper.createCompositionalLayout())
            collectionView.delegate = self
            collectionView.backgroundColor = .systemBackground
            collectionView.register(FollowersCollectionHeaderView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: FollowersCollectionHeaderView.reuseId)
            view.addSubview(collectionView)
            return collectionView
        }()
        
        fileprivate lazy var snapshot: NSDiffableDataSourceSnapshot<Section, Follower.ID> = {
            var snapshot = NSDiffableDataSourceSnapshot<Section, Follower.ID>()
            snapshot.appendSections([.main])
            let itemIdentifiers = followers.map { $0.id }
            snapshot.appendItems(itemIdentifiers, toSection: .main)
            dataSource.apply(snapshot, animatingDifferences: true)
            return snapshot
        }()
        
        fileprivate func updateData(with followers: [Follower]) {
            snapshot = NSDiffableDataSourceSnapshot<Section, Follower.ID>()
            snapshot.appendSections([.main])
            let itemIdentifiers = followers.map { $0.id }
            snapshot.appendItems(itemIdentifiers, toSection: .main)
            dataSource.apply(snapshot, animatingDifferences: true)
        }
        
        fileprivate func configureDataSource() {
            let cellRegistration = UICollectionView.CellRegistration<FollowerCell, Follower.ID> { [weak self]
                cell, indexPath, followerID in
                guard let self = self else { return }
                
                let followerArray = self.followers.filter { $0.id == followerID }
                
                if let follower = followerArray.first {
                    cell.set(on: follower)
                }
            }
            
            dataSource = UICollectionViewDiffableDataSource<Section, Follower.ID>(collectionView: collectionView) {
                collectionView, indexPath, itemIdentifier in
                
                return collectionView.dequeueConfiguredReusableCell(using: cellRegistration,
                                                                    for: indexPath,
                                                                    item: itemIdentifier)
            }
            
            let headerRegistration = createSectionHeaderRegistration()
            dataSource.supplementaryViewProvider = { collectionView, elementKind, indexPath in
                return collectionView.dequeueConfiguredReusableSupplementary(using: headerRegistration, for: indexPath)
            }
        }
        
        fileprivate func createSectionHeaderRegistration() -> UICollectionView.SupplementaryRegistration<FollowersCollectionHeaderView> {
            return UICollectionView.SupplementaryRegistration<FollowersCollectionHeaderView>(
                elementKind: FollowersCollectionHeaderView.reuseId) { [weak self] supplementaryView, elementKind, indexPath in
                guard let self = self else { return }
                supplementaryView.set(with: self.user)
            }
        }