2017-04-12 4 views
0

私はチャートにデータを提示することで過去30日間の2つの通貨間の為替レートを表示するアプリを作っています。私はAlamofireとSwiftyJSONを使って応答を解析しています。私が使用しているAPIはhttp://fixer.ioです。30件のAlamofireリクエストがすべて応答されたことを検出するにはどうすればよいですか?

私のビューコントローラーでは、ratesという辞書([Date: Double])を入力するgetRateというこのメソッドがあります。 getRateviewDidLoadに呼び出され、次のように定義されています

func getRate() { 
    let baseCurrency = UserDefaults.standard.string(forKey: "baseCurrency") 
    let formatter = DateFormatter() 
    formatter.dateFormat = "yyyy-MM-dd" 
    for date in last30Days { 
     let dateString = formatter.string(from: date) 
     let url = "https://api.fixer.io/\(dateString)?base=\(baseCurrency!)&symbols=\(self.currency!)" 
     Alamofire.request(url).responseString { 
      [weak self] 
      response in 
      if let _ = response.error { 
       return 
      } 

      let json = JSON(parseJSON: response.value!) 
      if let _ = json["error"].string { 
       return 
      } 

      if self != nil { 
       if let rate = json["rates"][self!.currency.currencyCode].double { 
        self!.rates[date] = rate 
       } 
      } 
     } 
    } 
} 

last30Daysは、過去30日間に、毎日のためにDateを格納し、タイプ[Date]の定数、です。 self.currencyは、別のView ControllerによってこのView Controllerに渡される列挙型の値です。 self.currency.currencyCodeは、enumで表される通貨の通貨コードを返す計算されたプロパティです。 "GBP"。基本的にgetRateは30件のリクエストを作成し、応答値をrates辞書に追加しています。

私がしたいのは、30件のリクエストがエラーなく返された後、為替レートに従ってチャートがプロットされます。現在、辞書に30のエントリがあるかどうかを確認します。存在する場合は、グラフをリフレッシュしてください:

var rates: [Date: Double] = [:] 
    { 
    didSet { 
     if rates.count == 30 { 
      refreshCharts() 
     } 
    } 
} 

「更新」ボタンを追加するまでは正常に機能しました。ユーザーがリフレッシュすると、30回同じリクエストをもう一度やりたいそれらがすべて正常に成功した場合は、ratesに新しい値を入力します。 1つ以上のエラーが発生した場合は、ratesの値を変更せずに保持する、つまり古いデータを保持します。

これを行うには、30のリクエストがすべて応答されたかどうか、エラーがあるかどうかを知る必要があります。 if rates.count == 30のトリックを使用することはできません。ユーザーが更新すると、ratesには既に古い値が設定されているためです。 ratesを最初に空にすることはできません。古いデータが失われるためです。エラーが発生した場合に表示する必要があります。 getRateの先頭にエラーがないことを保証するものではありません。

基本的に30件すべてのリクエストに応答したときとエラーが発生したかどうかを確認する方法

答えて

3

あなたはDispatchGroupを使用することができます。

let group = DispatchGroup() 
var errors: [Error] = [] 

for date in last30Days { 
    group.enter() 

    // ... 

    Alamofire.request(url).responseString { 
     defer { group.leave() } 
     guard let error = response.error else { 
      errors.append(error) 
      return 
     } 

     // ... 
    } 
} 

group.notify(queue: .main) { 
    // All requests are finished now 
} 

エラー配列は、(あなたの辞書と同じ)、スレッドセーフではありませんのでご注意ください。

編集:スレッドの安全性を確保するために、キューに更新をディスパッチして、一度に異なるスレッドから変数が変更されないようにすることができます。

let queue = DispatchQueue(label: "queue") 
var errors: [Error] = [] 
for date in last30Days { 
    // ... 
    queue.async { 
     errors.append(error) 
    } 
    // ... 
} 

あなたがスレッドの安全性についてもう少し説明でき辞書

+0

ために同じことを行うことができますか?何が起こる可能性がありますか? – Sweeper

+0

答えを編集して、スレッドの安全性をどのように達成できるかを示しました。基本的に、複数のキューから同時に変数に書き込もうとすると、これらの書き込みの一部を削除することができます。あなたのケースでは、たとえすべてのネットワーク要求が完璧に機能したとしても、30個の値のうち29個だけが存在する可能性があります。 – XML

+0

さて、 'self.rates'にはどこに応答値を割り当てるべきですか? 'group.notify'ブロックでは?しかし、私はそこの回答にアクセスすることはできませんよね? – Sweeper

関連する問題