Swift Tableview Pagination

Swift tableView Pagination

For that you need to have server side change also.

  1. Server will accept fromIndex and batchSize in the API url as query param.

    let listUrlString =  "http://bla.com/json2.php?listType=" + listType + "&t=" + NSUUID().UUIDString + "&batchSize=" + batchSize + "&fromIndex=" + fromIndex
  2. In the server response, there will be an extra key totalItems. This will be used to identify all items are received or not. An array or items fromIndex to batchSize number of items.

In the app side

  1. First loadItem() will be called with fromIndex = 0 and batchSize = 20 (for example in viewDidLoad() or viewWillAppear). removeAll items from privateList array before calling loadItem() for the first time

  2. Server returns an array of first 20 items and totalItems total number of items in the server.

  3. Append the 20 items in privateList array and reload tableView

  4. In tableView:cellForRowAtIndexPath method check if the cell is the last cell. And check if totalItems (form server) is greater than privateList.count. That means there are more items in the server to load

    if indexPath.row == privateList.count - 1 { // last cell
    if totalItems > privateList.count { // more items to fetch
    loadItem() // increment `fromIndex` by 20 before server call
    }
    }

Question: where is refresh ? will be scrolling ?

Refresh after appending new items in the array when server response received. (step 3)

Scrolling will trigger tableView:cellForRowAtIndexPath for every cell when user scrolls. Code is checking if it is the last cell and fetch remaining items. (step 4)

Sample project added:
https://github.com/rishi420/TableViewPaging

Swift 4 tableView API Pagination

There were many flaws in your logic, so I have rewritten it with minimal changes. Now it will serve your purpose.

import UIKit
struct PagedCharacters: Codable {
let info: Info
let results: [Results]

}
struct Info: Codable {
let count: Int
let pages: Int
let next: String
let prev: String
}

struct Results: Codable {
let name: String
let status: String
let species: String
}

And here is the code for view controller class:

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {

@IBOutlet var uiTableView: UITableView!
var aryDownloadedData:[Results]?
var nextPageUrl:String!

override func viewDidLoad() {
super.viewDidLoad()
getIntitalRickAndMortyData()


self.uiTableView.dataSource = self
self.uiTableView.delegate = self

// Do any additional setup after loading the view.
}
func getIntitalRickAndMortyData(){
aryDownloadedData = []
//here first page is next page
nextPageUrl = "https://rickandmortyapi.com/api/character/"
getRickAndMortyData()
}

func getRickAndMortyData() {

//construct the url, use guard to avoid nonoptional
guard let urlObj = URL(string: nextPageUrl) else
{ return }

//fetch data
URLSession.shared.dataTask(with: urlObj) {[weak self](data, response, error) in

//to avoid non optional in JSONDecoder
guard let data = data else { return }

do {
//decode object
let downloadedRickAndMorty = try JSONDecoder().decode(PagedCharacters.self, from: data)
self?.aryDownloadedData?.append(contentsOf: downloadedRickAndMorty.results)
self?.nextPageUrl = downloadedRickAndMorty.info.next

DispatchQueue.main.async {
self?.uiTableView.reloadData()
}
print(self?.aryDownloadedData as Any)

} catch {
print(error)

}

}.resume()

}


func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.aryDownloadedData?.count ?? 0
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

if let count = aryDownloadedData?.count, count>1{
let lastElement = count - 1
if indexPath.row == lastElement {
//call get api for next page
getRickAndMortyData()
}
}

guard let cell = tableView.dequeueReusableCell(withIdentifier: "rickandmortyCell") as? CharacterTableViewCell else { return UITableViewCell() }

cell.nameLabel.text = "Name: " + (self.aryDownloadedData?[indexPath.row].name ?? "default")

return cell
}
}

Pagination infinitely calling in TableView - Swift/Xcode

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.

Pagination in tableView in Swift

When you call your API with the take=20 parameter, it just returns first 20 items, and those 20 items also include the same items that the API would return with take=10. So when you append the new result to the previous result, you get duplicated items because your previous result already contained some of the items from the new result. It means that you need to replace the old data with the new data each time you call your API to avoid item duplication.

Pagination usually doesn't work like that. The API should typically accept either a range (something like start=10&end=20) or the page number (optionally with the page size, for example pagesize=10&pagenumber=2).

You should check if your API supports something like that.

Alternatively, if the number of returned items will never be huge, you can opt out from using pagination completely and fetch all items in one request.

If the number of items can be huge, but your API can only return first N items, you might keep the logic when you first fetch first N items, then 2N items, then 3N and so on and completely replace the data each time, but that is far from ideal.

Infinite scrolling pagination of the TableView

You can see that you have reached the bottom of the list with willDisplay function from the UITableViewDelegate protocol. like below code:

public func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forItemAt indexPath: IndexPath) {
let lastIndex = self.popularMoviesArray.count - 1
if indexPath.row == lastIndex {
// api call and get next page
}
}

and you can call API to get the next page data.
then append new data to your data source list and reload tableView:

self.popularMoviesArray.append(contentsOf: newListData)
self.tableView.reloadData()

Swift 4 tableview pagination for api data

You always replacing current data with new fetched data, you need to be append in it.

self.myAds = data!

instead of this you need to append new data in existing one.

self.myAds.append(contentsOf: data!)


Related Topics



Leave a reply



Submit