Reputation: 15
Let's say I have two classes: One class represents the database where I perform an HTTP GET request.
func getObjects(completion: (([Object]) -> Void)?) {
guard let url = // ...
let session = URLSession(configuration: .default)
let task = session.dataTask(with: url) { (data, response, error) in
DispatchQueue.main.async {
if let error = error {
fatalError("Dispatching main queue failed: \(error)")
} else if let data = data {
let decoder = JSONDecoder()
do {
let objects = try decoder.decode([Object].self, from: data)
completion?(object)
} // ...
// ...
}
task.resume()
}
The corresponding result object array is passed to a UIViewController which is a different class. Because I instantiate multiple databases based on different configuration objects, I want to display the results sorted in a sectioned UITableView.
func load(then: (([(DatabaseConfig?, [Object])]) -> Void)) {
var allObjects: [(DatabaseConfig?, [Object])] = [(nil, [])]
Storage.loadDatabaseConfigs(completion: { configs in
self.configs = configs
})
for config in self.configs {
var objectSection: (DatabaseConfig?, [Object]) = (nil, [])
let database = Database.init(config: config)
objectSection.0 = config
database.getObjects(completion: { objects in
objectSection.1.append(objects)
})
allObjects.append(allObjectsInSection)
}
then(allObjects)
}
The load and "formatting" works.
I call the load(then:)
function in viewWillAppear()
, where I pass the result to the class array which holds all final entries for the TableView. Then, I try to reload the data.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
self.load(then: { objects in
self.allObjects = objects
self.tableView.reloadData()
})
}
I am aware, that the GET request needs its time, so my question is: How or where do I reload the UITableView? I obviously need to be sure, that the GET request has been performed successfully and reload after that. I already tried to envoke DispatchQueue.main.async
for the reloading, but that didn't work.
Upvotes: 0
Views: 52
Reputation: 3937
Since you make several requests, you really don't want to call your then
closure until all requests have finished. You could probably use a DispatchGroup
for this.
func load(then: (([(DatabaseConfig?, [Object])]) -> Void)) {
var allObjects: [(DatabaseConfig?, [Object])] = [(nil, [])]
Storage.loadDatabaseConfigs(completion: { configs in
self.configs = configs
let dispatchGroup = DispatchGroup()
for config in self.configs {
dispatchGroup.enter()
var objectSection: (DatabaseConfig?, [Object]) = (nil, [])
let database = Database.init(config: config)
objectSection.0 = config
database.getObjects(completion: { objects in
objectSection.1.append(objects)
dispatchGroup.leave()
})
allObjects.append(allObjectsInSection)
}
dispatchGroup.notify(queue: .main) {
then(allObjects)
}
})
}
EDIT: As @Macistador noted, loadDatabaseConfigs is async, so the database calls should be made in the completion
callback of that function.
Upvotes: 2