I have an app that will fetch exactly 100 strings from an API and place them into a UITableView. I wish to first preload the data into an array and then, once the array is fully populated with the 100 entries, load the data into the table.
Due to the asynchronous API call, it seems like I am unable to load data into the array before the table view starts populating its cells. Mainly, I am having difficulty getting the data out of the closure in the first place.
This is the API call defined in an APIAgent
class:
func getAPIData(_ requestType: String, completionHandler: @escaping (Data) -> ()) {
let requestURL: URL = URL(string : baseURL + requestType)!
let currentSession = URLSession.shared
let task = currentSession.dataTask(with: requestURL) { (data, response, error) in
completionHandler(data!)
}
task.resume()
}
This is how the UITableView uses it:
protocol AsyncHelper {
func getData(data: Any)
}
class TableViewController: UITableViewController, AsyncHelper {
var dataEntries: [String] = []
func getData(data: Data) {
let entry: String = String(describing: data)
dataEntries.append(entry)
}
override func viewDidLoad() {
super.viewDidLoad()
for i in 1...100 {
apiAgent.getAPIData("entry" + String(i), entry: { entry in
self.getData(data: entry)
})
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "EntryCell", for: indexPath) as! EntryCell
let entry: String = dataEntries[indexPath.row] // index out of range error
DispatchQueue.main.async {
// add Strings to cell here
}
return cell
}
}
So it appears that the cells are being generated before data gets populated into the dataEntries
array. How do I prevent the UITableView from generating the cells until dataEntries
is populated.
Try this :
override func viewDidLoad() {
super.viewDidLoad()
tblView.delegate = nil
tblView.dataSource = nil
for i in 1...100 {
apiAgent.getAPIData("entry" + String(i), entry: { entry in
self.getData(data: entry)
tblView.delegate = self
tblView.dataSource = self
tblView.reloadData()
})
}
}
If you are going to use a closure you won't need a protocol. You could change your networking code to:
var songData = [Data]
func getAPIData(_ requestType: String, completionHandler: @escaping ([Data]) -> ()) {
let requestURL: URL = URL(string : baseURL + requestType)!
let currentSession = URLSession.shared
let task = currentSession.dataTask(with: requestURL) { (data, response, error) in
songData.append(data!)
if (songData.count == 100) {
completionHandler(songData)
}
}
task.resume()
}
This will make sure that your getData()
and tableView.reloadData()
will only be called once all 100 of your data elements have been loaded.
FYI - tableView.reloadData()
will reload pretty much everything that has to deal with your table view. Your numberOfRows
, numberOfSections
, and cellForRow
will all be called again. This will create the tableView over again using the updated dataEntries
values
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.