Ios 如何检测所有30个Alamofire请求都已响应?
我正在制作一个应用程序,通过在图表中显示数据来显示过去30天两种货币之间的汇率。我正在使用Alamofire和SwiftyJSON解析响应。我正在使用的API是 在我的视图控制器中,我有一个名为getRate的方法,它将填充一个名为rates的字典[Date:Double]。getRate将在viewDidLoad中调用,定义如下:Ios 如何检测所有30个Alamofire请求都已响应?,ios,swift,alamofire,Ios,Swift,Alamofire,我正在制作一个应用程序,通过在图表中显示数据来显示过去30天两种货币之间的汇率。我正在使用Alamofire和SwiftyJSON解析响应。我正在使用的API是 在我的视图控制器中,我有一个名为getRate的方法,它将填充一个名为rates的字典[Date:Double]。getRate将在viewDidLoad中调用,定义如下: func getRate() { let baseCurrency = UserDefaults.standard.string(forKey: "base
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天中每天的日期。self.currency是由另一个视图控制器传递到此视图控制器的枚举类型值。self.currency.currencyCode是一个计算属性,返回枚举表示的货币的货币代码,例如GBP。基本上,getRate发出30个请求,并将响应值添加到rates字典中
我想做的是,在所有30个请求都得到无误响应后,根据汇率绘制一张图表。目前,我检查字典是否有30个条目。如果是,请刷新图表:
var rates: [Date: Double] = [:]
{
didSet {
if rates.count == 30 {
refreshCharts()
}
}
}
在我想添加刷新按钮之前,这一切都很好。当用户刷新时,我想再次发出相同的30个请求。若它们都成功且无误,则使用新值填充速率。如果发生一个或多个错误,我希望保持费率值不变,即保留旧数据
要做到这一点,我必须知道所有30个请求何时都得到响应,是否有任何错误。我不能再使用if rates.count==30技巧,因为当用户刷新时,rates已经填充了旧值。我无法在开始时将速率设置为空,因为这将丢失旧数据,当出现任何错误时,需要显示旧数据。我不能保证在getRate开始时不会出现错误
基本上,我如何知道所有30个请求何时得到响应以及是否出现任何错误?您可以使用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)
}
// ...
}
您可以对词典执行相同的操作您可以使用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)
}
// ...
}
你可以对你的字典做同样的操作你能再解释一下线程安全吗?可能会发生什么?我编辑了答案以展示如何实现线程安全。基本上,如果您试图同时从多个队列写入变量,其中一些写入操作可能会被删除。在您的情况下,30个值中只有29个可以存在,即使所有网络请求都工作正常。好的,那么我应该在哪里将响应值分配给self.rates?在group.notify块中?但是我不能访问那里的响应,对吗?您应该在queue.async{}块中分配给self.rates。在group.notify块中,将正确填充rates和errors集合。您能再解释一下线程安全吗?可能会发生什么?我编辑了答案以展示如何实现线程安全。基本上,如果您试图同时从多个队列写入变量,其中一些写入操作可能会被删除。在您的情况下,30个值中只有29个可以存在,即使所有网络请求都工作正常。好的,那么我应该在哪里将响应值分配给self.rates?在group.notify块中?但是我不能访问那里的响应,对吗?您应该在queue.async{}块中分配给self.rates。在group.notify块中,将正确填充rates和errors集合