Pagination with Firebase firestore - swift 4

Related searches

I'm trying to paginate data (infinitely scroll my tableview) using firestore. I've integrated the code google gives for pagination as best I can, but I'm still having problems getting the data to load in correctly.

The initial set of data loads into the tableview as wanted. Then, when the user hits the bottom of the screen, the next "x" amount of items are load in. But when the user hits the bottom of the screen the second time, the same "x" items are simply appended to the table view. The same items keep getting added indefinitely.

So its the initial 3 "ride" objects, followed by the next 4 "ride" objects repeating forever.

123 4567 4567 4567 4567...

How do I get the data to load in correctly?

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    let offsetY = scrollView.contentOffset.y
    let contentHeight = scrollView.contentSize.height

    if offsetY > contentHeight - scrollView.frame.height {
        // Bottom of the screen is reached
        if !fetchingMore {
            beginBatchFetch()
        }
    }
}

func beginBatchFetch() {
    // Array containing "Ride" objcets is "rides"

    fetchingMore = true

    // Database reference to "rides" collection
    let ridesRef = db.collection("rides")

    let first = ridesRef.limit(to: 3)

    first.addSnapshotListener { (snapshot, err) in
        if let snapshot = snapshot {
            // Snapshot isn't nil
            if self.rides.isEmpty {
                // rides array is empty (initial data needs to be loaded in).
                let initialRides = snapshot.documents.compactMap({Ride(dictionary: $0.data())})
                self.rides.append(contentsOf: initialRides)
                self.fetchingMore = false
                DispatchQueue.main.asyncAfter(deadline: .now() + 5, execute: {
                    self.tableView.reloadData()
                })
                print("first rides loaded in")
            }
        } else {
            // Error
            print("Error retreiving rides: \(err.debugDescription)")
            return
        }

        // reference to lastSnapshot
        guard let lastSnapshot = snapshot!.documents.last else{
            // The collection is empty
            return
        }


        let next = ridesRef.limit(to: 4).start(afterDocument: lastSnapshot)

        next.addSnapshotListener({ (snapshot, err) in
            if let snapshot = snapshot {

                if !self.rides.isEmpty {

                    let newRides = snapshot.documents.compactMap({Ride(dictionary: $0.data())})
                    self.rides.append(contentsOf: newRides)
                    self.fetchingMore = false
                    DispatchQueue.main.asyncAfter(deadline: .now() + 7, execute: {
                        self.tableView.reloadData()
                    })

                    print("new items")
                    return
                }
            } else {
                print("Error retreiving rides: \(err.debugDescription)")
                return
            }

        })
    }
}

So here's the solution I've come up with! It is very likely that this solution makes multiple calls to firestore, creating a large bill for any real project, but it works as a proof of concept I guess you could say.

If you have any recommendations or edits, please feel free to share!

Here's how all the variables were initialized:

var rides = [Ride]()
var lastDocumentSnapshot: DocumentSnapshot!
var fetchingMore = false

If you have any recommendations or edits, please feel free to share!

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    let offsetY = scrollView.contentOffset.y
    let contentHeight = scrollView.contentSize.height
    //print("offsetY: \(offsetY) | contHeight-scrollViewHeight: \(contentHeight-scrollView.frame.height)")
    if offsetY > contentHeight - scrollView.frame.height - 50 {
        // Bottom of the screen is reached
        if !fetchingMore {
            paginateData()
        }
    }
}

// Paginates data
func paginateData() {

    fetchingMore = true

    var query: Query!

    if rides.isEmpty {
        query = db.collection("rides").order(by: "price").limit(to: 6)
        print("First 6 rides loaded")
    } else {
        query = db.collection("rides").order(by: "price").start(afterDocument: lastDocumentSnapshot).limit(to: 4)
        print("Next 4 rides loaded")
    }

    query.getDocuments { (snapshot, err) in
        if let err = err {
            print("\(err.localizedDescription)")
        } else if snapshot!.isEmpty {
            self.fetchingMore = false
            return
        } else {
            let newRides = snapshot!.documents.compactMap({Ride(dictionary: $0.data())})
            self.rides.append(contentsOf: newRides)

            //
            DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: {
                self.tableView.reloadData()
                self.fetchingMore = false
            })

            self.lastDocumentSnapshot = snapshot!.documents.last
        }
    }
}

Paginate data with query cursors, For example, if you use startAt(A) in a query, it returns the entire alphabet. If you use startAfter(A Duration: 10:16 Posted: Oct 3, 2017 With query cursors in Cloud Firestore, you can split data returned by a query into batches according to the parameters you define in your query. Query cursors define the start and end points for a query, allowing you to:

My solution was similar to @yambo, however, I tried to avoid making extra calls to the database. After the first call to the database, I get back 10 objects and when it is time to load the new page I kept a reference of how many objects and I checked if the count + 9 is in the range of my new count.

    @objc func LoadMore() {
    let oldCount = self.uploads.count
    guard shouldLoadMore else { return }
    self.db.getNextPage { (result) in
        switch result {
        case .failure(let err):
            print(err)
        case .success(let newPosts):
            self.uploads.insert(contentsOf: newPosts, at: self.uploads.count)
            if oldCount...oldCount+9 ~= self.uploads.count {
                self.shouldLoadMore = false
            }
            DispatchQueue.main.async {
                self.uploadsView.collectionView.reloadData()
            }
        }
    }
}

Table View Infinite Scrolling using Firestore Paging, Specially, you will use Firestore as your back-end with paging feature that you will not need to… May 8, 2019 � 4 min read. Image for post Create an app on Firebase Console, download GoogleService-Info.plist and import into the project. Open UserTableViewController.swift and add the following block of code into. With query cursors in Firestore, you can split data returned by a query into batches according to the parameters you define in your query. Query cursors define the start and end points for a query, allowing you to:

A little late in the game, but I would like to share how I do it, using the query.start(afterDocument:) method.

class PostsController: UITableViewController {

    let db = Firestore.firestore()

    var query: Query!
    var documents = [QueryDocumentSnapshot]()
    var postArray = [Post]()

    override func viewDidLoad() {
        super.viewDidLoad()

        query = db.collection("myCollection")
                  .order(by: "post", descending: false)
                  .limit(to: 15)

        getData()
    }

    func getData() {
        query.getDocuments() { (querySnapshot, err) in
            if let err = err {
                print("Error getting documents: \(err)")
            } else {
                querySnapshot!.documents.forEach({ (document) in
                    let data = document.data() as [String: AnyObject]

                    //Setup your data model

                    let postItem = Post(post: post, id: id)

                    self.postArray += [postItem]
                    self.documents += [document]
                })
                self.tableView.reloadData()
            }
        } 
    }

    func paginate() {
        //This line is the main pagination code.
        //Firestore allows you to fetch document from the last queryDocument
        query = query.start(afterDocument: documents.last!)
        getData()
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return postArray.count
    }

    override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
        // Trigger pagination when scrolled to last cell
        // Feel free to adjust when you want pagination to be triggered
        if (indexPath.row == postArray.count - 1) {
            paginate()
        }
    }
}

Result like so:

Here is a reference.

Ordering & Paginating Data - Swift + Firebase Part 8, Project files: http://replicode.io/docs/swift-&-firebase-project/8-pagination/ Previous tutorial Duration: 19:58 Posted: Aug 28, 2018 In this Video i'm going to show how to create a App That Fetches Data From Firestore Using Pagination In SwiftUI | Shimmer Loading Effect Using SwiftUI | SwiftUI Firebase Firestore Tutorial

Firestore Pagination - It Just Got Easier, Learn how to run pagination queries in Firestore forward and backward. Use it here: https Duration: 6:41 Posted: Nov 26, 2019 Pagination is the process of dividing data into discrete pages. In Firestore, it is achieved by ordering a collection by a field, limiting it to a consistent page size, then offsetting the query. The Firebase Web SDK v7.3.0 introduced a new limitToLast (n) method that makes the process much easier.

Firebase iOS Pagination, Github files: https://github.com/hedgehog-labs/iOS- Show less How Do I Paginate Duration: 11:03 Posted: Apr 28, 2017 There will (almost) never be a time that I load the profile and don't need the images, so going with firebase storage would require me to do 7 reads (1 Firestore + 6 storage), instead of just 1. Also just like tinder I'm going to be using photos that are less than 100kb a pop, so it will easily fit under the 1mb cap.

Pass data from tableView Cell to another view controller Xcode 9.0 (Swift 4.0) - Duration: 22:35. Let Create An App 37,544 views