Home > Net >  Pagination infinitely calling in TableView - Swift/Xcode
Pagination infinitely calling in TableView - Swift/Xcode

Time:03-13

I have a TableView that displays documents from my Firebase Firestore server. Instead of downloading ALL of the documents on the server, I paginate and download more documents as the user scrolls down the TableView. This works great if there are a lot of documents on the server. If there are only a few documents, however, meaning there's not enough for the user to need to scroll through, then the pagination process is infinitely called, infinitely querying documents from the server. How can I fix this? I know it's an issue with the following line in tableView willDisplayForRowAt:

if (indexPath.row == fileArray.count - 1)

I just don't know how to fix it.

struct FileIdentifierStruct {
    var fileName = String()
    var fileDate = String()
}

var fileArray = [FileIdentifierStruct]()

func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
        // Trigger pagination when scrolled to last cell
        if (indexPath.row == fileArray.count - 1) {
            print("Calling paginate")
            paginate()
        }
    }

func paginate() {
        print("paginate function was called")
        // This is the main pagination code
        query = query.start(afterDocument: documents.last!)
        // For some reason calling getData here enters an infinite loop until the view is exited
        getData()
    }

func getData() {
        print("getData was called")
        query.getDocuments() { (querySnapshot, err) in
            if let err = err {
                print("Error getting documents: \(err.localizedDescription)")
                let alert = SCLAlertView()
                alert.showError("ERROR", subTitle: "The following error occured while trying to retrieve the documents: \(err.localizedDescription)")
            } else {
                querySnapshot!.documents.forEach({ (document) in
//                    let data = document.data() as [String: AnyObject]
                    
                    // Set up the data modal here
                    let date = document.get("Date") as? String ?? ""
                    let id = document.documentID
                    let fileItem = FileIdentifierStruct(fileName: id, fileDate: date)
                    self.fileArray  = [fileItem]
                    self.documents  = [document]
                })
                self.filesTableView.reloadData()
                self.filesTableView.showDefault()
            }
        }
    }

CodePudding user response:

You can add a boolean flag:

var hasReceivedZeroResults = false

Then inside your getData() function when you receive the documents list:

self.hasReceivedZeroResults = querySnapshot!.documents.isEmpty

Finally insert at the top of your paginate() function:

guard !hasReceivedZeroResults else { return }

CodePudding user response:

The challenge here is that the actual dataset in Firebase Firestore has an unknown amount of data, so without knowing what the 'last row' is, there's no easy way to know you're displaying the 'last row' when paginating

A couple of options:

A common solution is to have another collection that stores a count of documents for the collection you're displaying, and observe that collection for changes so you always know what the last row is and can not attempt to load more data when you're displaying the last row of data.

For example suppose your app displays users

users_collection
   user_0
      name: "Jay"
   user_1
      name: "Cindy"
   user_2
      name: "Larry"

and then a collection that keeps track of the number of users

document_count
   users_collection
      count: 3

Add an observer to the document_count, users_collection and as users are added or removed, increment/decrement that count, keep a class var to make that count available to functions, call it var totalRows = 0

Then, it becomes a matter of only calling paginate when the user scrolls to the last row in the array AND there's more data available

func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt...
   // Trigger pagination when scrolled to last cell & there's more rows
   if (indexPath.row == fileArray.count - 1 && lastRowDisplayedIndex < self.totalRows) {
      paginate()
   }
}

Note this assumes you're tracking what rows from Firestore are being displayed in some form.

Another option is to keep a count of how many documents were read in on the last query

query.getDocuments() { (querySnapshot, err) in
   self.lastReadCount = snapshot.count

then when the user attempts to scroll, paginate will only be called if the lastReadCount is equal to the number you want displayed.

For Example

Suppose you want to display 5 users at a time. If 5 users are being displayed then call paginate to attempt to read 5 more, if only 3 are read then self.lastReadCount will be 3 and you'll know you're at the end of the list, so don't call paginate.

  • Related