2017-11-12 5 views
1

これで、応答に別の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が返されます。

私が理解できないことは、この問題の良い解決策です。

+0

結果を呼び出し側に返すために 'getPlanet(uri:String)'メソッドに 'エスケープクロージャー 'を追加します。サーバーからデータを正常に受信すると、そのクロージャを呼び出します。 https://stackoverflow.com/a/45976392/5912335 –

+0

それで、あなたは、クラスAPIコール元のオブジェクトに結果を戻す '@エスケープ処理 'としてアノテーション付けされたクロージャをタスク内に置くべきですか? –

+0

正確に。メソッド定義から 'getPlanet'メソッドの戻り値(' - > String')を削除してください。それは必要ではありません。 –

答えて

1

getPlanetは非同期呼び出しを実行しています。 resultインスタンスは、受信したデータを保持しません。代わりに、getPlanetで完了ブロックを使用し、データを受け取ると、この完了ブロックが呼び出されます。このようなもの。 closuresを読んでください。

+0

ありがとうございました。だから私の理解が正しい場合、私は関数のパラメータとして文字列引数を取る "完了"という名前でクロージャを渡します。 @エスケープアノテーションは、そのクロージャがメソッドをエスケープし、getCharacterメソッド内から引数にアクセスできるようにします。私のgetPlanet関数では、結果にアクセスする必要があるときはいつでもクロージャを呼び出すようにしています。そりゃ素晴らしい。 –

+0

はい。 Swiftへようこそ! –