我正在创建一个应用程序,通过在图表中显示数据,向您显示过去30天内两种货币之间的汇率。我正在使用Alamofire和SwiftyJSON来解析响应。我使用的API是http://fixer.io。
在我的视图控制器中,我有一个名为getRate
的方法,它将填充一个名为[Date: Double]
的字典(rates
)。 getRate
将调用viewDidLoad
,其定义如下:
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
是[Date]
类型的常量,在过去30天内每天存储Date
。 self.currency
是由另一个视图控制器传递给此视图控制器的枚举类型值。 self.currency.currencyCode
是一个计算属性,它返回枚举所代表的货币的货币代码,例如"GBP"
。基本上,getRate
发出了30个请求,并将响应值添加到rates
字典中。
我想要做的是在没有错误的情况下响应所有30个请求后,根据汇率绘制图表。目前,我检查字典是否有30个条目。如果是,请刷新图表:
var rates: [Date: Double] = [:]
{
didSet {
if rates.count == 30 {
refreshCharts()
}
}
}
这个工作正常,直到我想添加"刷新"按钮。当用户刷新时,我想再次发出相同的30个请求。如果它们都成功且没有错误,请使用新值填充rates
。如果发生一个或多个错误,我希望保持rates
的值不变,即保留旧数据。
要做到这一点,我必须知道所有30个请求何时被响应以及是否有任何错误。我不能再使用if rates.count == 30
技巧了,因为当用户刷新时,rates
已经填充了旧值。我无法在开头将rates
设置为空,因为这会丢失旧数据,当出现任何错误时需要显示旧数据。我无法保证在getRate
开头没有错误。
基本上,如何知道何时响应所有30个请求以及是否发生错误?
答案 0 :(得分:4)
您可以使用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
}
请注意,errors数组不是线程安全的(与字典相同)。
编辑:为了线程安全,您可以在队列上调度更新,以确保不会立即从不同的线程修改变量。
let queue = DispatchQueue(label: "queue")
var errors: [Error] = []
for date in last30Days {
// ...
queue.async {
errors.append(error)
}
// ...
}
你可以为你的词典做同样的事情