Reputation: 237
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!
Upvotes: 7
Views: 5621
Reputation: 720
My issue was that I was loading my snapshot programmatically by iterating over a dictionary's keys and getting an index for my sections. One of my sections didn't have any data so I skipped adding it to the snapshot. But then my index was off on the iteration. In essence, it expected the wrong number of items for that key. I changed to a reg int counter and then only added when I added sections and that fixed it.
Upvotes: 0
Reputation: 2698
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.
Upvotes: 1
Reputation: 52
in this function private func applySnapshot(structure:[SectionItem]) use applySnapshot with sectionIdentifier
Upvotes: 0
Reputation: 14338
In my case, my data source is set up wrongly. I'm returning more sections than I actually provide in UICollectionViewCompositionalLayout(sectionProvider: sectionProvider)
.
Upvotes: 2
Reputation: 975
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.
Upvotes: 1
Reputation: 52
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
Upvotes: 2
Reputation: 580
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.
Upvotes: 2