2017-12-04 3 views
-1

OMDb APIを使用してGame of Thronesエピソード概要を作成しようとしています。私は情報をサーバからうまく取得していますが、私はそのデータをUITableViewに送信するのが困難です。私は正確に同じ方法論を使用してSWのAPI(スターウォーズ)と同様の作業を行っているし、それがその後、働いスウィフト3エスケープクロージャーの変化が固まらない

func getAllForSeason(season: Int) { 
    let uri = "\(baseURL)\(urlId)&season=\(season)" 
    var episodeList = [EpisodeModel]() 
    let url = URL(string: uri) 
    let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in 
     if error != nil { 
     print("Error retrieving information. No season found") 
     } else { 
     if let content = data { 
      do { 
      let seasonJSON = try JSONSerialization.jsonObject(with: content, options: .mutableContainers) as? [String: Any] 
      let episodesFound = seasonJSON?["Episodes"] as! [[String: String]] 
      for episodeJSON in episodesFound { 
       let number = Int(episodeJSON["Episode"] ?? "0") ?? 0 
       var episodeToAdd = EpisodeModel() 
       self.getTemporaryEpisode(season: season, number: number, completion: {(result:EpisodeModel) in 
       episodeToAdd = result 
       print(episodeToAdd.season) 
       }) 
       print(episodeToAdd.season) 
       episodeList.append(episodeToAdd) 
      } 
      DispatchQueue.main.async { 
       self.delegate?.didGetSeason(episodes: episodeList) 
      } 
      } catch { 
      print ("Error retrieving information. No seasons found") 
      } 
     } 
     } 
    } 
    task.resume() 
    } 


private func getTemporaryEpisode(season: Int, number: Int, completion: @escaping(_ response:EpisodeModel)->Void) { 
    let episode = EpisodeModel() 
    let uri = "\(baseURL)\(urlId)&season=\(season)&episode=\(number)" 
    let url = URL(string: uri) 
    let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in 
     if error != nil { 
     print("Error downloading episode information. Empty Episode returned") 
     } else { 
     if let content = data { 
      do { 
      let episodeJSON = try JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableContainers) as? [String: Any] 
      episode.title = (episodeJSON?["Title"] as? String)?.description ?? "" 
      let dateString = (episodeJSON?["Released"] as? String)?.description ?? "" 
      let dateFormatter = DateFormatter() 
      dateFormatter.dateFormat = "yyyy MMM dd" 
      episode.releaseDate = dateFormatter.date(from: dateString) ?? Date() 
      episode.rating = (episodeJSON?["Rated"] as? String)?.description ?? "" 
      episode.season = Int(episodeJSON?["Season"] as? String ?? "") ?? 0 
      episode.number = Int(episodeJSON?["Episode"] as? String ?? "") ?? 0 
      let runtimeString = (episodeJSON?["Runtime"] as? String)?.description ?? "0 min" 
      let spaceIndex = runtimeString.index(of: " ") ?? runtimeString.endIndex 
      let minuteString = runtimeString[runtimeString.startIndex..<spaceIndex] 
      episode.minutes = Int(minuteString) ?? 0 
      episode.director = (episodeJSON?["Director"] as? String)?.description ?? "" 
      episode.writer = (episodeJSON?["Writer"] as? String)?.description ?? "" 
      let actorsString = (episodeJSON?["Actors"] as? String)?.description ?? "" 
      episode.actors = actorsString.components(separatedBy: ", ") 
      episode.plot = (episodeJSON?["Plot"] as? String)?.description ?? "" 
      } catch { 
      print("Error downloading episode information. Empty or incomplete episode returned") 
      } 
     } 
     } 
     completion(episode) 
    } 
    task.resume() 
    } 

これは閉鎖し、それを定義するものを呼び出すの両方の関数です。

試験のために配置された印刷文は両方秒、しかし、私は最初のprint文プリントながら適切に(クロージャ内から)に代え見つけるgetTemporaryEpisode

を用いて得られたエピソードのシーズン番号を印刷する必要がありますクロージャーの外側の1つは0(値が初期化されていることを確認するために設定したデフォルト値)です。つまり、episodeToAddには結果が含まれません。

たとえば、Winter Is Comingはエンクロージャ内で1を出力しますが、そのうちの0が出力されます。

最初に0が出力されるので、スレッドの問題で、2番目のprint文が呼び出されるまでに完了していないと確信しています。私はそれを解決する方法だけではわかりません。

編集:私は愚かなループのメインスレッドへの私の呼び出しを置いたが、この問題を修正しなかった修正を実現しました。

答えて

0

あなたの関数getTemporaryEpisodeは非同期なので、空のエピソードの配列でデリゲートに通知します。

episodeToAdd変数を適切に設定されたエピソードを参照するように再割り当てしますが、内容はepisodeListには影響しません。適切な内容を取得するまで、episodeListの入力を待つことをお勧めします。これにはDispatchGroupを使用できます。

ただし、単にDispatchGroupを使用した場合、エピソードはそれぞれ同時に独立してリクエストされるため、予定された順番に戻ってくる必要はありません。最も簡単な解決策は、それらを配列に集めてから、デリゲートに通知する前にエピソード番号で配列をソートすることです。

+0

申し訳ありませんが、それらを見たことがありません。しかし、大雑把に言えば、 'getTemporaryEpisode'の各呼び出しは、' dispatchGroup.enter() 'のようなものになり、' dispatchGroup.leave() 'が完了クロージャ内にあり、' dispatchGroupループの最後に.wait()を追加します。 これが正しい場合は、代わりにエンクロージャーに戻ってきたリストに追加して、ループが完了したら代理人に通知する前に配列を並べ替えることができます。 –

+0

はい、あなたは 'DispatchGroup'で正しい考えを持っています。ポイントは、 'getTemporaryEpisode'呼び出しのどれかが完了ハンドラを呼び出す前にループが終了することです。 'DispatchGroup.wait()'が必要なのは、すべての補完ハンドラが返るまでコードが継続しないようにするためです。 –

関連する問題