CollectionView error : attempt to insert item 40 into section 0, but there are only 40 items in section 0 after the update

1.6k views Asked by At

The collection view will show list of 20 movies and if user scrolls to the last item, then it'll load another 20. At first I used reloadData() after appending another 20 items to the arrays of movies, And it works. But I believe the correct way to do it is to insertItems() to the collectionView instead of calling reloadData() every single time. After I implemented the insertItems(), Then the error occurs.

Here's the code related to the CollectionView :

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return moviesArray.count
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MovieCell", for: indexPath) as! MovieCell
        let imageUrl = moviesArray[indexPath.row].posterPath!
        let url = URL(string: "https://image.tmdb.org/t/p/w500\(imageUrl)")!
        cell.moviePoster.load(url: url)
        cell.layer.cornerRadius = 10
        return cell
    }
    
    func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
        if indexPath.row == moviesArray.count - 1 {
            currentPage += 1
            dataManager.downloadAllMoviesJSON(page: currentPage)
        }
    }
    
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        dataManager.downloadMovieDetailJSON(id: moviesArray[indexPath.row].id) { (data) in
            DispatchQueue.main.async {
                self.performSegue(withIdentifier: "ToMovieDetail", sender: self)
            }
        }

And here's the code that handling the movies list loading and appending :

    func didGetMovies(dataManager: DataManager, movie: MovieData) {
        if currentPage > 1 {
            let lastInArray = moviesArray.count
            self.moviesArray.append(contentsOf: movie.results)
            let newLastInArray = moviesArray.count
            let indexPaths = Array(lastInArray...newLastInArray).map{IndexPath(item: $0, section: 0)}
            DispatchQueue.main.async {
                self.moviesCV.performBatchUpdates({
                    self.moviesCV.insertItems(at: indexPaths)
                }, completion: nil)
            }
        } else {
            moviesArray = movie.results
            DispatchQueue.main.async {
                self.moviesCV.reloadData()
            }
        }
    }

At first I didn't know that the insertItems() must be inside the performBatchUpdates() but even if I do so, The error still persists. It might be because I've implemented it improperly.

The error is always :

Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'attempt to insert item 40 into section 0, but there are only 40 items in section 0 after the update'

Perhaps anyone could kindly show the part that seems to be the issue?

1

There are 1 answers

0
vadian On BEST ANSWER

The last index of an array is .count - 1. You have to use the ..< operator

let indexPaths = Array(lastInArray..<newLastInArray).map{IndexPath(item: $0, section: 0)}

and the performBatchUpdates block is not needed, it's only useful for simultaneous insert/delete/move operations

DispatchQueue.main.async { 
    self.moviesCV.insertItems(at: indexPaths) 
}