Swift iOS: Firebase Paging

后端 未结 2 1615
灰色年华
灰色年华 2020-12-14 04:41

I have this Firebase data:

I want to query the posts data through pagination. Currently my code is converting this JS code to Swift code



        
2条回答
  •  陌清茗
    陌清茗 (楼主)
    2020-12-14 05:18

    I know I'm a bit late and there's a nice answer by timominous, but I'd like to share the way I've solved this. This is a full example, it isn't only about pagination. This example is in Swift 4 and I've used a nice library named CodableFirebase (you can find it here) to decode the Firebase snapshot values.

    Besides those things, remember to use childByAutoId when creating a post and storing that key in postId(or your variable). So, we can use it later on.

    Now, the model looks like so...

        class FeedsModel: Decodable {
            var postId: String!
            var authorId: String! //The author of the post
            var timestamp: Double = 0.0 //We'll use it sort the posts.
            //And other properties like 'likesCount', 'postDescription'...
        }
    

    We're going to get the posts in the recent first fashion using this function

        class func getFeedsWith(lastKey: String?, completion: @escaping ((Bool, [FeedsModel]?) -> Void)) {
            let feedsReference = Database.database().reference().child("YOUR FEEDS' NODE")
            let query = (lastKey != nil) ? feedsReference.queryOrderedByKey().queryLimited(toLast: "YOUR NUMBER OF FEEDS PER PAGE" + 1).queryEnding(atValue: lastKey): feedsReference.queryOrderedByKey().queryLimited(toLast: "YOUR NUMBER OF FEEDS PER PAGE")
            //Last key would be nil initially(for the first page).
    
            query.observeSingleEvent(of: .value) { (snapshot) in
                guard snapshot.exists(), let value = snapshot.value else {
                    completion(false, nil)
                    return
                }
                do {
                    let model = try FirebaseDecoder().decode([String: FeedsModel].self, from: value)
                    //We get the feeds in ['childAddedByAutoId key': model] manner. CodableFirebase decodes the data and we get our models populated.
                    var feeds = model.map { $0.value }
                    //Leaving the keys aside to get the array [FeedsModel]
                    feeds.sort(by: { (P, Q) -> Bool in P.timestamp > Q.timestamp }) 
                    //Sorting the values based on the timestamp, following recent first fashion. It is required because we may have lost the chronological order in the last steps.
                    if lastKey != nil { feeds = Array(feeds.dropFirst()) }
                    //Need to remove the first element(Only when the lastKey was not nil) because, it would be the same as the last one in the previous page.
                    completion(true, feeds)
                    //We get our data sorted and ready here.
                } catch let error {
                    print("Error occured while decoding - \(error.localizedDescription)")
                    completion(false, nil)
                }
            }
        }
    

    Now, in our viewController, for the initial load, the function calls go like this in viewDidLoad. And the next pages are fetched when the tableView will display cells...

        class FeedsViewController: UIViewController {
    
            //MARK: - Properties
            @IBOutlet weak var feedsTableView: UITableView!
    
            var dataArray = [FeedsModel]()
            var isFetching = Bool()
            var previousKey = String()
            var hasFetchedLastPage = Bool()
    
    
            //MARK: - ViewController LifeCycle
            override func viewDidLoad() {
                super.viewDidLoad()
                //Any other stuffs..
                self.getFeedsWith(lastKey: nil) //Initial load.
            }
    
            //....
    
            func getFeedsWith(lastKey: String?) {
    
                guard !self.isFetching else {
                    self.previousKey = ""
                    return
                }
                self.isFetching = true
    
                FeedsModel.getFeedsWith(lastKey: lastKey) { (status, data) in
                    self.isFetching = false
                    guard status, let feeds = data else {
                        //Handle errors
                        return
                    }
    
                    if self.dataArray.isEmpty { //It'd be, when it's the first time.
                        self.dataArray = feeds
                        self.feedsTableView.reloadSections(IndexSet(integer: 0), with: .fade)
                    } else {
                        self.hasFetchedLastPage = feeds.count < "YOUR FEEDS PER PAGE"
                        //To make sure if we've fetched the last page and we're in no need to call this function anymore.
                        self.dataArray += feeds
                       //Appending the next page's feed. As we're getting the feeds in the recent first manner.
                        self.feedsTableView.reloadData()
                    }
                }
            }
    
            //MARK: - TableView Delegate & DataSource
    
            //....
    
            func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
                if self.dataArray.count - 1 == indexPath.row && !self.hasFetchedLastPage {
                let lastKey = self.dataArray[indexPath.row].postId
                guard lastKey != self.previousKey else { return }
                //Getting the feeds with last element's postId. (postId would be the same as a specific node in YourDatabase/Feeds).
                self.getFeedsWith(lastKey: lastKey)
                self.previousKey = lastKey ?? ""
            }
    
            //....
        }
    

提交回复
热议问题