So I'm having an issue running an API call whose response includes another API call.
Here's the first function:
class APICaller{
weak var delegate:APIDelegate?
func getCharacter(x:Int){
let character = CharacterModel()
let url = URL(string: "https://swapi.co/api/people/\(x)/")
let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in
if error != nil{
print("Error downloading character information. Empty character returned.")
} else {
if let content = data {
do{
let charJSON = try JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableContainers) as? [String: Any]
character.name = (charJSON?["name"] as? String)?.description ?? ""
character.height = Int((charJSON?["height"] as? String)?.description ?? "0") ?? 0
character.mass = Int((charJSON?["mass"] as? String)?.description ?? "0") ?? 0
character.hairColor = (charJSON?["hair_color"] as? String)?.description ?? ""
character.skinColor = (charJSON?["skin_color"] as? String)?.description ?? ""
character.eyeColor = (charJSON?["eye_color"] as? String)?.description ?? ""
character.birthYear = (charJSON?["birth_year"] as? String)?.description ?? ""
character.gender = (charJSON?["gender"] as? String)?.description ?? ""
character.homeWorld = self.getPlanet(uri: (charJSON?["homeworld"] as? String)?.description ?? "")
//The homeward part of the response is another URL and as such requires another API Call to get anything meaningful
DispatchQueue.main.async {
self.delegate?.didGetStarWarsCharacter(characterData:character)
}
}catch{
print("Error downloading character information. Empty or incomplete character returned")
}
}
}
}
task.resume()
}
private func getPlanet(uri:String)->String{
if uri == ""{
return uri // return empty string if the original call doesn't get anything.
}
var result = ""
let url = URL(string:uri)
let task = URLSession.shared.dataTask(with: url!){(data,response,error)->Void in
if error != nil{
result = "No Planet Found"
}else{
if let planet = data{
do{
let planetJSON = try JSONSerialization.jsonObject(with: planet, options: JSONSerialization.ReadingOptions.mutableContainers) as? [String:Any]
//print(planetJSON?["name"] as? String ?? "No Planet")
result = (planetJSON?["name"] as? String)?.description ?? "No Planet Found"
}catch{
result = "No Planet Found"
}
}
}
}// end of task, result is lost due to multithreading
task.resume()
return result
}
}
So I understand that running the task for getPlanet
happens on another thread, and this method returns before before the task can finish running. As such when the delegate gets the CharacterModel, its homeWorld parameter is empty.
For example, if I were to call print(character.homeWorld)
after the getPlanet
function runs I would receive an empty String.
What I can't figure out is a good solution to this problem.