2

Background

In my app, I store a bunch of object IDs. I use these IDs to make batch API calls. The API limits each call to 10 ID numbers. This data is rendered on a UITableView. The user can add and delete objects, which adds or removes the object ID from the database.

I’m using a Firestore database to store the object IDs on my end.

Current Implementation

Here’s what I’ve implemented so far, but it crashes the app when add & deleting objects. I haven’t been able to work out how to properly handle these cases & whether this is the right pattern to do something like this.

  1. Get object IDs to be used for making API calls
var objectIds: [String] = []
var chunkedObjectIds: [[String]] = []
var objects: [Array] = []
var offset: Int = 0

override func viewDidLoad() {
    super.viewDidload()

    getObjectIds()

 }

func getObjectIds() {
    // get objects IDs and store then in objectIds from the Firestore database
    // setup the .addSnapshotLister so the query is triggered whenever there is a change in the data on Firestore for the collection

    return chunkedObjectIds

    // when finished, get the first 10 objects from the 3rd party API

    fetchObjects()

}
  1. Take object Ids array, split into array of arrays (lots of 10) & Make the API call for the first 10
func fetchObjects() {

     // split objectIds array in array of arrays, in lots of 10
     // chunkedObjectIds is set here

     // request the objects for the first 10 ID numbers

    Alamofire.request(… parameter with first 10 object ids …) (objects) in {

        // save objects

        // increment the offset
        offset += 1
    }

}
  1. Render the data on the UITableView cells

  2. Use the following method to load more data from the 3rd party API:

 func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {

        let lastRow = objects.count 

        var parameters = [String: Any]()

        if indexPath.row == lastRow {
            if !(offset == self.chunkedObjectIds.count) {

                // process the next batch from the array
                parameters["id-numbers"] = self.chunkedObjectIds[offset].map{String($0)}.joined(separator: ",")

                Alamofire.request(… paramaters: parameters) { (objects) in
                    for item in 0..<objects.count {
                        let indexPath = IndexPath(row: item + self.objects.count, section: 0)
                        self.paths.append(indexPath)
                    }
                    self.objects.append(contentsOf: objects)
                    self.tableView.beginUpdates()
                    self.tableView.insertRows(at: self.paths, with: .automatic)
                    self.tableView.endUpdates()
                    self.paths.removeAll()
                    self.offset += 1
                }
            }
        }
    }
  1. Adding or deleting objects:

    • The object ID is added or deleted from the Firestore database
    • The objectIds, chunkedObjectIds, offset and objects are cleared
    • The listener triggers a read of the data and the process repeats

The Issue & Question

This works well to load initial data. But duplication occurs when adding (and sometimes crashing). When deleting the app will crash because of out of range exceptions.

Is this the correct pattern to use in the first place? If so, what am I missing to handle cases after the first load, specifically the addition and deletion of new object IDs.

Edit

I have changed the implementation based on feedback in the comments. So now, the process is like this:

  1. Setup listener to get data from Firestore
  2. Loop through the object ids from Firestore and while the counter is < 10 or we reach object.count - Now I save the next offset and the next time it triggers this method, I initiate a loop from the next offset with the same while conditions
  3. Fetch the objects from the 3rd party API
  4. I kept using willDisplay cell method to trigger more data to load - it seemed to work more reliably than scrollDidEnd method.

So now the app doesn't crash anymore. There are some issues with the firestore listener, but I'll post that as a separate question.

  • Instead of creating an array of arrays, why don't you remember the last fetched object and get the index of it and fetch the next 10? – Sachin Vas Mar 22 '19 at 07:11
  • try to divide and conquer. Mean first implement pagination correctly. Then try to implement add or delete. this will make your life easier. Secondly, i prefer to use scrollView delegate function `scrollViewDidEndDragging ` for detecting end of tableView and reload more data. see [this question](https://stackoverflow.com/q/5137943/7698092). BTW, I prefer [this](https://stackoverflow.com/a/17860149/7698092) approach – Awais Fayyaz Mar 22 '19 at 07:17
  • https://github.com/alfianlosari/SWPaginationIOS , i have try it – Puji Wahono Mar 22 '19 at 07:58
  • @SachinVas I ended up using that method instead, it's a much better way! Thanks. – KeepTryingMike Mar 23 '19 at 03:30
  • 1
    @AwaisFayyaz - Thanks for the tip... I tried that method, but I went back to ```WillDisplay cell``` - I felt it works slightly better. – KeepTryingMike Mar 23 '19 at 03:31
  • @PujiWahono - Good method. In my case I don't get any next offset or next url from the API to use for the next call. – KeepTryingMike Mar 23 '19 at 03:32

0 Answers0