2017-12-04 1 views
-1

OMDb API를 사용하여 Game of Thrones 에피소드 개요를 작성하려고합니다. 서버에서 정보를 얻고 있지만 UITableView에 데이터를 보내는 데 어려움을 겪고 있습니다. 나는 동일한 방법을 사용하여 SW의 API (스타 워즈)와 유사한 작업을 수행하고 그 다음 일Swift 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() 
    } 

:

이 폐쇄하고 정의하는 하나를 호출 두 기능입니다.

모두 그러나 getTemporaryEpisode

얻은 에피소드의 시즌 번호를 인쇄해야 테스트를 위해 배치 인쇄 문 , 내가 찾을 대신에 (폐쇄 내에서) 제대로 첫 번째 인쇄 문 인쇄, 두 번째 동안 closure 외부의 오른쪽은 0 (값이 초기화되었는지 확인하기 위해 설정 한 기본값)을 인쇄합니다. 즉, episodeToAdd에 더 이상 결과가 포함되지 않습니다.

예 : Winter Is Coming은 인클로저에서 1을 인쇄하지만 그 중 0은 인쇄합니다.

먼저 0을 인쇄하므로 스레드 문제이며 두 번째 print 문이 호출 될 때 완료되지 않은 것입니다. 나는 그것을 해결하는 방법을 모르겠습니다.

편집 : 나는 어리 석음으로 루프 내부의 주 스레드에 대한 호출을 배치했지만이 문제를 수정하지는 못했습니다.

답변

0

사용자의 기능 getTemporaryEpisode은 비동기식이므로 비어있는 에피소드 배열로 대리인에게 알립니다.

그런 다음 episodeToAdd 변수를 올바르게 다시 설정하면 올바르게 설정된 에피소드를 참조 할 수 있지만 내용은 episodeList에 영향을주지 않습니다. 적절한 내용을 검색 할 때까지 episodeList을 채우기를 기다리는 것이 좋습니다. 이 경우 DispatchGroup을 사용할 수 있습니다.

그러나 단순히 DispatchGroup을 사용하면 에피소드가 각각 독립적으로 동시에 요청되므로 에피소드를 예상 한 순서대로 되돌릴 필요가 없습니다. 가장 쉬운 해결책은 배열로 배열 한 다음 에피소드 번호별로 배열을 정렬하여 대리인에게 알리는 것입니다.

+0

죄송합니다, 이전에 본 적이 없습니다. 요약하자면, 여러분은'getTemporaryEpisode'에 대한 각 호출이'dispatchGroup.enter()'와 같은 것이어야하고, 완료 폐쇄 내에'dispatchGroup.leave()'가 있어야하고,'dispatchGroup .wait()'루프의 끝에서. 맞으면 인클로저에서 다시 보내는 목록에 추가 한 다음 루프가 완료되면 내 대리자에게 알리기 전에 배열을 정렬 할 수 있습니다. –

+0

예, 당신은'DispatchGroup'을 가지고 올바른 생각을 가지고 있습니다. 핵심은'getTemporaryEpisode' 호출이 완료 핸들러를 호출하기 전에 루프가 완료된다는 것입니다. 모든 완료 핸들러가 반환 될 때까지 코드가 계속되는 것을 막기 위해서는'DispatchGroup.wait()'이 필요합니다. –

관련 문제