Multiple Async APIs to populate uitableview in desired order iOS swift

1.2k Views Asked by At

I have multiple API calls that will update the uitableview whenever they fetch the results. UI needs to be updated as and when API gives the data. All API calls are async. The data must be populated in the right order. API0 should update section 0, API1 should update section 1 and so on.

I've been able to achieve this with 2 APIs but when I use the 3rd API, I face crashes.

Please find my code below:

    @IBOutlet weak var myTableView: UITableView!
var myDataSource: myTableDataSource!
var initialLoad = true
var tablD = [Int : [Any]]()
let queue = DispatchQueue(
    label: "com.affluvar.multipleAPI.MyQueue", // 1
    attributes: .concurrent)

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.
    myTableView.tableFooterView = UIView()
    API0()
    API1()
    API2()
}

//MARK: API methods
func API0(){
    queue.async {
        print("queue THREAD0")
        getData(offset: 0,limit: 10){
            (finalArray) in
            print("IN FIRST")
            for rest in finalArray{
                print(rest.name)
            }
            self.queue.sync {
                self.tablD.updateValue(finalArray, forKey: 0)
                self.myDataSource = myTableDataSource(data: self.tablD)
                print("SYNC DATASOURCE UPDATED 0")
                DispatchQueue.main.async {
                    if self.initialLoad == true{
                        self.initialLoad = false
                        self.myTableView.dataSource = self.myDataSource
                        print("TABLE RELOADING INITIAL ")
                        self.myTableView.reloadData()
                    }
                }
                self.saveData(data: finalArray, section: 0)
            }
        }

    }
}

func API1(){
    queue.async{
        print("queue THREAD1 ")
        getData(offset: 10,limit: 12){
            (finalArray1) in
            print("IN SECOND ")
            for rest in finalArray1{
                print(rest.name)
            }
            self.queue.sync{
                self.tablD.updateValue(finalArray1, forKey: 1)
                self.myDataSource = myTableDataSource(data: self.tablD)
                print("SYNC DATASOURCE UPDATED 1")
                DispatchQueue.main.async {
                    if self.initialLoad == true{
                        self.initialLoad = false
                        self.myTableView.dataSource = self.myDataSource
                        print("TABLE RELOADING INITIAL ")
                        self.myTableView.reloadData()
                    }
                }
                self.saveData(data: finalArray1, section: 1)
            }

        }
    }
}

func API2(){
    queue.async{
        print("queue THREAD2")
        getData(offset: 20,limit: 12){
            (finalArray1) in
            print("IN third")
            for rest in finalArray1{
                print(rest.name)
            }
            self.queue.sync{
                self.tablD.updateValue(finalArray1, forKey: 2)
                self.myDataSource = myTableDataSource(data: self.tablD)
                print("SYNC DATASOURCE UPDATED 2")
                DispatchQueue.main.async {
                    if self.initialLoad == true{
                        self.initialLoad = false
                        self.myTableView.dataSource = self.myDataSource
                        print("TABLE RELOADING INITIAL ")
                        self.myTableView.reloadData()
                    }
                }
                self.saveData(data: finalArray1, section: 2)
            }
        }
    }
}

func saveData(data:[userModel], section: Int){
        print("queue THREAD barrier ", section)
        //print("table datasrc entry:", self.myTableView.dataSource ?? "nil")
        DispatchQueue.main.sync {
                print("In else ", section)
                    print("sections before IN ELSE",self.myTableView.numberOfSections)
                    self.myTableView.dataSource = self.myDataSource
                    //self.myTableView.reloadData()
                    //self.myTableView.beginUpdates()
                    print("reloading section number:", section)
                    self.myTableView.reloadSections([section], with: .automatic)
                    print("sections AFTER in ELSE",self.myTableView.numberOfSections)
                    //self.myTableView.endUpdates()
        }
        print("CONTINUING QUEUE WORK")
}

Above is my viewController code.

Error here is ->

2018-08-01 16:13:14.381174+0530 docAnywhere[4299:118229] *** Assertion failure in -[UITableView _createPreparedCellForGlobalRow:withIndexPath:willDisplay:], /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKit_Sim/UIKit-3698.33.6/UITableView.m:13456 Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'UITableView dataSource is not set'

Table datasource class ->>

class myTableDataSource: NSObject, UITableViewDataSource{
var userData = [Any]()
var tableData = [[Any]]()
var tablD = [Int : [Any]]()
init(data: [Int : [Any]]) {
    tablD = data
}
//MARK: Table methods
func numberOfSections(in tableView: UITableView) -> Int {
    //print("sections:", tablD.count)
    return 3
}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    print("reload in progress")
    if let rows = tablD[section] as? [Any]{
        return rows.count
    }
    return 0
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "usersCell") as! usersCell
    let rowData = tablD[indexPath.section] as! [Any]
    let thisUser = rowData[indexPath.row] as? userModel// userData[indexPath.row] as? userModel
    cell.userName?.text = thisUser?.name ?? ""
    print("cell no", thisUser?.name ?? "", "at", indexPath.section, indexPath.row)
    if (indexPath.row == (rowData.count - 1)) {
        print("LAST CELL RELOAD COMPLETE HERE", indexPath.section)
    }
    return cell
}

}

Sometimes the error is this :-

2018-08-01 16:47:53.467219+0530 docAnywhere[6084:147469] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'attempt to delete section 0, but there are only 0 sections before the update'

NOTE:: It works fine in case of 2 API calls and dispatch group can't be used as the UI needs to be updated as soon as any API call finishes and data is available. Multiple array or datasource can't be used for multiple APIs.

1

There are 1 best solutions below

1
On

You're crashing because you're changing your tableView datasource constantly. I'm missing a piece of code in your code listing... If you can redesign you datasource so you don't have to change it like that you'll be fine. For example, you can reload section '1' with api1, and when it finishes you simply append the items you received from that api call to section 1... All this might sound confusing :) My point is, don't change the data source...