【问题标题】:How to detect that all 30 Alamofire requests have all been responded to?如何检测所有 30 个 Alamofire 请求都已得到响应?
【发布时间】:2017-04-12 10:15:44
【问题描述】:

我正在制作一个应用程序,通过在图表中显示数据来显示过去 30 天内两种货币之间的汇率。我正在使用 Alamofire 和 SwiftyJSON 来解析响应。我使用的 API 是http://fixer.io

在我的视图控制器中,我有一个名为 getRate 的方法,它将填充一个名为 rates 的字典 ([Date: Double])。 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 天内的每一天存储一个Dateself.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 个请求何时都得到响应以及是否发生任何错误

【问题讨论】:

    标签: ios swift alamofire


    【解决方案1】:

    您可以使用 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)
        }
        // ...
    }
    

    你可以对你的字典做同样的事情

    【讨论】:

    • 你能多解释一下线程安全吗?可能会发生什么?
    • 我编辑了答案以展示如何实现线程安全。基本上,如果您尝试同时从多个队列写入变量,则可以删除其中一些写入。在您的情况下,30 个值中只有 29 个可能存在,即使所有网络请求都运行良好。
    • 好的,那么我应该在哪里将响应值分配给self.rates?在group.notify 块中?但我无法访问那里的回复,对吧?
    • 您应该在queue.async { } 块内分配给self.rates。在group.notify 块中,将正确填充速率和错误集合
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-11-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-31
    • 2015-08-14
    • 1970-01-01
    相关资源
    最近更新 更多