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.
- 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()
}
- 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
}
}
Render the data on the UITableView cells
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
}
}
}
}
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:
- Setup listener to get data from Firestore
- 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
- Fetch the objects from the 3rd party API
- I kept using
willDisplay cell
method to trigger more data to load - it seemed to work more reliably thanscrollDidEnd
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.