Swift - InstantSearch - collectionView... numberOfItemsInSection called multiple times

Hello everyone,

I am using InstantSearch to fetch data and display them in a collection view. To do that, I am using the code below:

class ProfileView: UIView, HitsController {
var singleIndexHitsInteractor: HitsInteractor = .init(showItemsOnEmptyQuery: true )
var challengeCollectionView: UICollectionView?
public weak var hitsSource: HitsInteractor?

private func setupChallengeIndexSearchSettings() {
singleIndexHitsInteractor.connectSearcher(challengesIndexSearcher)
}

private func searchCreatedChallenges() {
    var filterState = FilterState()
    filterState = setupFilter(indexName: "challenges", challengeState: SELECTED_CHALLENGE_STATE)

    print(filterState.getFilters())
    challengesIndexSearcher.connectFilterState(filterState)
    challengesIndexSearcher.query = searchBarController.searchBar.text
    challengesIndexSearcher.search()
}


 func scrollToTop() {
    if (hitsSource?.numberOfHits() == 0) {
        return
    }
    self.challengeCollectionView?.scrollToItem(at: IndexPath(row: 0, section: 0), at: .top, animated: true)
}
// FUNCTION BELOW IS CALLED MULTIPLE TIMES AFTER CELLFORROW..
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    if (hitsSource?.numberOfHits() == 0) {
        addNoChallengesLabel()
        return 0
    }
    else {
        removeNoChallengesLabel()
        return (hitsSource?.numberOfHits())!
    }
}
 func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let myChallengesCell = collectionView.dequeueReusableCell(withReuseIdentifier: "myChallenges", for: indexPath) as! MyChallengesCollectionViewCell

    if (challengeStateSegmentedControl.selectedSegmentIndex == 0) {
        let challengeObject = Challenge(json: (hitsSource?.hit(atIndex: indexPath.row))!)
        print(challengeObject.id)
       
        myChallengesCell.setChallengeTitle(challengeObject:challengeObject)
       ...

Using the code above, I get the results I want. First

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int)

is called and then

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

But what I don’t understand - why is numberOfItemsInSection called again after going through three times (as expected) within cellForItemAt? I mean, in the next step, it is going again three times but I thought since we have the datasource set, there is no reason call that function as long as you don’t make a refresh?

I observed following in the logs
"The request timed out." UserInfo={NSUnderlyingError=0x280c3f2a0 {Error Domain=kCFErrorDomainCFNetwork Code=-1001 "(null)" UserInfo={_kCFStreamErrorCodeKey=-2102, _kCFStreamErrorDomainKey=4}}, NSErrorFailingURLStringKey=https://XXX-dsn.algolia.net/1/indexes/*/queries,**

Not sure why a timeout happens. I get the objects anyway but could it be that setting the source is called multiple times because of a retry-mechanism?

Thanks in advance!
Take care and stay healthy

Ok, setting a breakpoint in the method

func reload() {
   challengeCollectionView?.reloadData()
}

Show that it is called multiple times. I had to implement the function above because of using HitsController. Can anyone tell me why it is called multiple times?

Hello guys, does anyone have an idea? Thanks

Hello @kaan548,

Multiple reasons may lead to extra reload() method calls.

Most likely this happens due to the infinite scrolling implementation. Depending on the pagination settings, the HitsInteractor may fetch the additional hits pages to ensure the smooth infinite scrolling experience. The reception of each new hits page causes the reload of the hits controller. There is a room for optimization here, e.g. we can call reload() method only in case of changes of the visible hits, but that would significantly complicate the HitsController protocol complexity, something we prefer to avoid.

Another reason could be the redundant connections between your HitsInteractor and HitsController. Make sure that HitsInteractor.connectController method is called just once.