6

I'm currently working on a simple IOS app using a collection view and diffable datasource.

I initalize the everything in the view controller's config and call a reloadView-function every time I update the source data.

The app crashes with the following error as soon as I call the reloadView function. But only if the collection view was empty before. If there are already items in there everything works perfectly fine.

Assertion failure in -[_UICollectionCompositionalLayoutSolver _queryClientForSectionDefintionForSectionIndex:]


Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid section definition. Please specify a valid section definition when content is to be rendered for a section. This is a client error.'

This is what my code looks like:


private func configureHierarchy() {

        let layout = collectionViewHelper.createLayout(structure: self.structure)

        collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: layout)
        collectionView.delegate = self

        collectionView.register(RecentCollectionViewCell.self, forCellWithReuseIdentifier: RecentCollectionViewCell.reuseIdentifier)
        collectionView.register(OverviewCollectionViewCell.self, forCellWithReuseIdentifier: OverviewCollectionViewCell.reuseIdentifier)
        collectionView.register(MessageItemCollectionViewCell.self, forCellWithReuseIdentifier: MessageItemCollectionViewCell.reuseIdentifier)
        view.addSubview(collectionView)
    }

private func configureDataSource() {

        dataSource = UICollectionViewDiffableDataSource<SectionType, Int>(collectionView: collectionView) {
            (collectionView: UICollectionView, indexPath: IndexPath, cellIndex: Int) -> UICollectionViewCell? in

            let sectionItem = self.structure[indexPath.section]

            switch sectionItem.type{
                //Returns a collection view cell of the specific type
            case .recent:
                return getRecentItemCell(...)
            case .overview:
                return getOverviewItemCell(...)
            case .message:
                return getMessageItemCell(...)
            default:
                return nil
            }
        }
    }

I apply the snapshot like this

private func applySnapshot(structure:[SectionItem]) {
        var snapshot = NSDiffableDataSourceSnapshot<SectionType, Int>()
        var itemOffset = 0

        for structurItem in structure {
            snapshot.appendSections([structurItem.type])
            snapshot.appendItems(Array(itemOffset..<itemOffset + structurItem.items))
            itemOffset += structurItem.items
        }

        dataSource.apply(snapshot, animatingDifferences: false)
    }

I do this once in my configure

structure = createStructure()
configureHierarchy()
configureDataSource()
applySnapshot(structure: createStructure())

This is the reload function I call every time the data changes (The error is thrown if there was no data displayed before)

func reloadView() {
        structure = createStructure()
        applySnapshot(structure: structure)
    }

Any ideas why this is? Thanks a lot already!

Schurigeln
  • 189
  • 1
  • 7

6 Answers6

3

In my case, my data source is set up wrongly. I'm returning more sections than I actually provide in UICollectionViewCompositionalLayout(sectionProvider: sectionProvider).

Hlung
  • 13,850
  • 6
  • 71
  • 90
2

In my case, the UICollectionViewCompositionalLayout is not deleted in memory, it continues to work when my controller is already deleted.

private func configureLayout() {
    let layout = UICollectionViewCompositionalLayout { [weak self] (sectionIndex: Int, layoutEnvironment: NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection? in
        return self?.layoutSection(sectionIndex: sectionIndex, layoutEnvironment: layoutEnvironment)
    }
    self.collectionView.collectionViewLayout = layout
}

its my code

  • This was my issue. My configureLayout() method was part of a class, LayoutManager. The LayoutManager object I created to assign the compositional layout was being reset to nil. And with it, the CollectionView's layout. My solution was to change LayoutManager to a struct, instead of a class. – theogood Dec 03 '21 at 16:36
1

Here's the int _UICollectionCompositionalLayoutSolver! It's got nothing to do with your diffable data source. You probably haven't completed the implementation of your composable layout yet. Which is what just happened to me :) That just happens to be where the exception is triggered.

iamsim.me
  • 560
  • 4
  • 17
1

Similar to Khoren's answer, in my case, there was a memory leak in my implementation. Instead of the view controller holding a reference to the data source, I had another object implementing and holding a reference to the data source. This eventually caused a cyclic memory reference (memory leak).

The solution was when the view controller was being dismissed (perhaps on viewDidDisappear), I set the datasource = nil; therefore, breaking the cyclic reference cycle and avoiding this error.

deniz
  • 725
  • 10
  • 13
0

in this function private func applySnapshot(structure:[SectionItem]) use applySnapshot with sectionIdentifier

0

I got a similar crash when calling reloadData(), somehow the closure on my UICollectionViewCompositionalLayout(sectionProvider:) returned old sections.

How I fixed it was calling collectionView.collectionViewLayout.invalidateLayout() before the reload.

rgkobashi
  • 2,551
  • 19
  • 25