Proper way to update collection view datasource for multiple call swift 5

1.8k views Asked by At

I have a collection view and I am loading 20 cells in every call (Fetching) from firebase in it. So I have two sections where the first section is the count number of my source array and the second section is just showing the loading cell when a bool value fetch more is true.

This is the collection view code:

extension ProductViewController: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
    func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 2
    }

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        if section == 0 {
            return self.objectArray.count
        } else if section == 1 {
            return fetchingMore ? 1 : 0
        }
        return 0
    }

    func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
        cellHeights[indexPath] = cell.frame.size.height
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
        return UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
        return 0
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
        var minimumLineSpacingForSectionAt: CGFloat
        if section == 0 {
            minimumLineSpacingForSectionAt = -50
        } else {
            minimumLineSpacingForSectionAt = 0
        }
        return minimumLineSpacingForSectionAt
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        if indexPath.section == 0 {
            return CGSize(width: productCollectionView.bounds.width, height: CommonService.heightForRowAtIndexPathForiPhones(cellType: .itemCell))
        } else {
            return CGSize(width: productCollectionView.bounds.width, height: 70)
        }
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        if indexPath.section == 0 {
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ItemAlbumCollectionViewCell", for: indexPath) as! ItemAlbumCollectionViewCell
            cell.setUpCell(product: objectArray[indexPath.row]!)
            return cell
        } else {
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "LoadingCollectionViewCell", for: indexPath) as! LoadingCollectionViewCell
            cell.activityIndicator.startAnimating()
            return cell
        }
    }

    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        let productCategoryViewController = Storyboards.ProductCategoryVC.controller as! ProductCategoryViewController
        productCategoryViewController.products = objectArray[indexPath.row]!
        pushViewController(T: productCategoryViewController)
    }

    func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {

        switch kind {
        case UICollectionView.elementKindSectionHeader:
            guard let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "StreachyCollectionHeaderReusableView", for: indexPath) as? StreachyCollectionHeaderReusableView else { fatalError("Invalid view type") }
            return headerView
        default:
            assert(false, "Invalid element type")
        }
    }
}

And this the function which is called for fetching data:

func pagination(forDivisionCollection: String) {
    fetchProduct(forDivisionCollection: forDivisionCollection, completion: { newData in
        self.productCollectionView.reloadSections(IndexSet(integer: 1))
        self.objectArray.append(contentsOf: newData)
        self.endReached = newData.count == 0
        self.fetchingMore = false

        UIView.performWithoutAnimation {
            self.productCollectionView.reloadData()
        }
    })
}

And it is working perfectly.

Now I have added a top menu section with a couple of options.

enter image description here

If a user taps on that section the function func pagination(forDivisionCollection: String) will be called with a parameter and it will filter data source with that parameter and populate data source according to the top menu option like this:

func pagination(isForNewDivision: Bool, forDivisionCollection: String) {
    self.fetchingMore = true
        if isForNewDivision == true {
            self.objectArray.removeAll()
        }
    fetchProduct(forDivisionCollection: forDivisionCollection, completion: { newData in
        self.productCollectionView.reloadSections(IndexSet(integer: 1))
        self.objectArray.append(contentsOf: newData)
        self.endReached = newData.count == 0
        self.fetchingMore = false

        UIView.performWithoutAnimation {
            self.productCollectionView.reloadData()
        }
    })
}

But I am getting 'Invalid update: invalid number of items in section 0. warning on Xcode when running it in the simulator and it is getting crashed on the physical device. I have researched a bit and come up with the solution of using performBatchUpdates but none of them are working. So my question is in this kind of situation what would be the perfect way to fetch data and load in collection view? Thanks a lot in advance.

0

There are 0 answers