KevinVuD
KevinVuD

Reputation: 611

Pull to refresh crash app because index out of bounds

I implement pull to refresh in collection View and the problem I'm facing is my app will crash with out of index message. Below is my cellForItem method

    override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! CauNguyenCell
    cell.postArray = postData[indexPath.item]
    return cell
}

    override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return postData.count
}

I know the problem is because I use removeAll method to clear all data from my postData but I have to that so my data array will have completely new data.

Below is my refresh function:

func handleRefresh(_ refreshControl: UIRefreshControl) {
    refreshControl.beginRefreshing()
    postData.removeAll()
    fetchDataAgain()
    self.collectionView.reloadData()
    refreshControl.endRefreshing()
}

error message: Thread 1: Fatal error: Index out of range

I just want to ask if anyone has any suggestions to solve the problem. Thanks!

Upvotes: 1

Views: 730

Answers (3)

Sergey Hleb
Sergey Hleb

Reputation: 172

Edit you code like this:

func fetchDataAgain(completion: ((Bool) -> ())) {
        // your code
        if postData != nil, postData.count != 0 {
            completion(true)
        }
    }

    func handleRefresh(_ refreshControl: UIRefreshControl) {
        refreshControl.beginRefreshing()
        postData.removeAll()
        fetchDataAgain { (complete) in
            if complete {
                self.collectionView.reloadData()
                refreshControl.endRefreshing()
            }
        }
    }   

Method fetchDataAgain will check array, and if it != nil and count != 0 in handler complete reloadData.
Code do all step by step, and when you are reload data in collection, your array can be empty, or nil. As a rule better to use handlers

Upvotes: 0

Sagar Chauhan
Sagar Chauhan

Reputation: 5823

I have implemented same thing in my project. First I have created refreshControl instance globally, then setup in initSetup() method call from viewDidLoad() in my view controller.

var refreshControl            : UIRefreshControl?
var arrWeddingInvitations     = [MyModelClass]()

func initialSetup() {
    self.refreshControl = UIRefreshControl()
    self.refreshControl?.tintColor = .yellow
    self.refreshControl?.addTarget(self, action: #selector(getWeddingInvitations), for: .valueChanged)
    self.cvWeddingInvitation.addSubview(refreshControl!)
}

This is call getWeddingInvitations() method which is fetching data from server.

// This code will hide refresh controller
DispatchQueue.main.async {
    self.refreshControl?.endRefreshing()
}

// This code in my API request completion block and will check responded array data, If it is not nil then assigned to my global array in view controller which are used to display data and reload collection view.
if arrInvitations != nil, arrInvitations.count > 0 {
    self.lblEmptyData.isHidden = true
    self.cvWeddingInvitation.isHidden = false
    self.arrWeddingInvitations = arrInvitations                                    
    self.cvWeddingInvitation.reloadData()
} else {
    self.lblEmptyData.isHidden = false
    self.cvWeddingInvitation.isHidden = true
}

This is working code in my current project. I hope this will help you.

See following video:

Pull to refresh test video

Upvotes: 2

Misternewb
Misternewb

Reputation: 1096

Please check your UICollectionViewDataSource implementation for these methods:

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

func numberOfSections(in: UICollectionView)

In the first one you should return the current number of items postData.count, the second one in your case should return 1.

Upvotes: 0

Related Questions