これで、応答に別のAPI呼び出しが含まれるAPI呼び出しを実行する際に問題が発生します。APIスレッドの問題iOS
ここで第一の機能です:
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
}
}
だから私はgetPlanet
のためのタスクを実行すると、別のスレッド上で発生し、タスクが実行を終了する前にこのメソッドが返す前にことを理解しています。デリゲートがCharacterModelを取得するとき、そのhomeWorldパラメータは空です。
たとえば、getPlanet
関数を実行した後にprint(character.homeWorld)
を呼び出すと、空のStringが返されます。
私が理解できないことは、この問題の良い解決策です。
結果を呼び出し側に返すために 'getPlanet(uri:String)'メソッドに 'エスケープクロージャー 'を追加します。サーバーからデータを正常に受信すると、そのクロージャを呼び出します。 https://stackoverflow.com/a/45976392/5912335 –
それで、あなたは、クラスAPIコール元のオブジェクトに結果を戻す '@エスケープ処理 'としてアノテーション付けされたクロージャをタスク内に置くべきですか? –
正確に。メソッド定義から 'getPlanet'メソッドの戻り値(' - > String')を削除してください。それは必要ではありません。 –