重试URLSession dataTask的模式?

bmt*_*033 7 ios nsurlsession swift nsurlsessiondatatask retry-logic

我是iOS/Swift开发的新手,我正在开发一个向REST API提出多个请求的应用程序.以下是检索"消息"的其中一个调用的示例:

func getMessages() {

    let endpoint = "/api/outgoingMessages"

    let parameters: [String: Any] = [
        "limit" : 100,
        "sortOrder" : "ASC"
    ]

    guard let url = createURLWithComponents(endpoint: endpoint, parameters: parameters) else {
        print("Failed to create URL!")
        return
    }

    do {
        var request = try URLRequest(url: url, method: .get)

        let task = URLSession.shared.dataTask(with: request as URLRequest) { (data, response, error) in

            if let error = error {
                print("Request failed with error: \(error)")
                // TODO: retry failed request
            } else if let data = data, let response = response as? HTTPURLResponse {                
                if response.statusCode == 200 {
                    // process data here
                } else {
                    // TODO: retry failed request
                }
            }
        }

        task.resume()

    } catch {
        print("Failed to construct URL: \(error)")
    }
}
Run Code Online (Sandbox Code Playgroud)

当然,此请求可能由于多种原因而失败(服务器无法访问,请求超时,服务器返回200以外的其他内容等).如果我的请求失败,我希望能够重试它,甚至可能在下一次尝试之前延迟.我在Apple的文档中没有看到关于这种情况的任何指导,但我发现了几个关于SO的相关讨论.不幸的是,这两个都是几年前和Objective-C,我从来没有与之合作过.是否有任何常见的模式或实现在Swift中做这样的事情?

jar*_*kes 7

我将处理重试的三种方法分类:

  1. 可达性重试
  • 可达性是一种说“当网络连接发生变化时让我知道”的奇特方式。Apple 对此有一些片段,但它们看起来并不有趣——我的建议是使用类似 Ashley Mill 的Reachability替代品。
  • 除了可达性之外,Apple 还提供了一个waitsForConnectivity(iOS 11+) 属性,您可以在URLSession配置上设置该属性。通过设置它,您会URLSessionDataDelegate在任务等待网络连接时收到警报。您可以利用这个机会启用离线模式或向用户显示某些内容。
  1. 手动重试
  • 让用户决定何时重试请求。我会说这是最常见的使用“拉动刷新”手势/用户界面来实现的。
  1. 定时/自动重试
  • 等待几秒钟,然后重试。
  • Apple 的Combine 框架提供了一种重试失败的网络请求的便捷方法。请参阅使用组合处理 URL 会话数据任务结果
  • 来自Apple Docs: Life Cycle of a URL Session (deprecated) ... 但是,您的应用不应立即重试 [a request]。相反,它应该使用可达性 API 来确定服务器是否可达,并且只有在收到可达性已更改的通知时才应发出新请求。

  • 好的评论。为此,我建议添加有关何时应该使用每种方法的信息。如果某个操作是由用户发起的“获取数据”操作,并且用户正在等待响应,则您应该显示错误,并允许用户选择何时重试,而不是让屏幕意外刷新。如果某个操作是由用户发起的“发布数据”操作,则应用程序应告诉用户它现在无法发布,但当用户上线时会自动发布。对于后台请求,请始终使用可达性。 (2认同)

bra*_*ipt 5

这个问题是在基于意见的方面播出,并且相当广泛,但我敢打赌大多数都是相似的,所以这里就是这样.

对于触发UI更改的数据更新:

(例如,填充了数据或图像加载的表格)一般的经验法则是以非阻碍的方式通知用户,如下所示:

然后有一个pull-to-refresh控件或刷新按钮.

对于不影响用户操作或行为的后台数据更新:

你可以根据代码轻松地在你的请求结果中添加一个重试计数器 - 但是我要小心这个并建立一些更智能的逻辑.例如,给定以下状态代码,您可能希望以不同方式处理事物:

  • 5xx:您的服务器出了问题.您可能希望延迟重试30秒或一分钟,但如果它发生3或4次,您将要停止锤击您的后端.

  • 401:经过身份验证的用户可能无法再被授权调用您的API.你根本不想重试这个; 相反,您可能希望将用户注销,以便下次他们使用您的应用时,系统会提示他们重新进行身份验证.

  • 网络超时/丢失连接:在重新建立连接之前,重试无关紧要.您可以在可达性处理程序周围编写一些逻辑,以便在下次网络连接可用时对后台请求进行排队.

最后,正如我们在评论中提到的那样,您可能希望查看通知驱动的后台应用程序刷新.这是在不对轮询服务器进行更改的情况下,您可以发送通知,告诉应用程序即使在前台未运行时也要自行更新.如果你足够聪明,你可以让你的服务器重复通知你的应用程序,直到应用程序确认收到 - 这将以一致的方式解决连接失败和无数其他服务器响应错误代码.