简体   繁体   English

重试 URLSession dataTask 的模式?

[英]Pattern for retrying URLSession dataTask?

I'm fairly new to iOS/Swift development and I'm working on an app that makes several requests to a REST API.我对 iOS/Swift 开发还很陌生,我正在开发一个向 REST API 发出多个请求的应用程序。 Here's a sample of one of those calls which retrieves "messages":这是检索“消息”的其中一个调用的示例:

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)")
    }
}

Of course, it's possible for this request to fail for a number of different reasons (server is unreachable, request timed out, server returns something other than 200, etc).当然,此请求可能因多种不同原因而失败(服务器无法访问、请求超时、服务器返回 200 以外的其他内容等)。 If my request fails, I'd like to have the ability to retry it, perhaps even with a delay before the next attempt.如果我的请求失败,我希望能够重试它,甚至可能在下一次尝试之前延迟。 I didn't see any guidance on this scenario in Apple's documentation but I found a couple of related discussions on SO.我在 Apple 的文档中没有看到关于这种情况的任何指导,但我发现了一些关于 SO 的相关讨论。 Unfortunately, both of those were a few years old and in Objective-C which I've never worked with.不幸的是,这两个都是几年前的,并且在我从未使用过的 Objective-C 中。 Are there any common patterns or implementations for doing something like this in Swift?在 Swift 中是否有任何常见的模式或实现来做这样的事情?

This question is airing on the side of opinion-based, and is rather broad, but I bet most are similar, so here goes.这个问题是基于意见的一面,而且相当广泛,但我敢打赌大多数是相似的,所以这里是。

For data updates that trigger UI changes:对于触发 UI 更改的数据更新:

(eg a table populated with data, or images loading) the general rule of thumb is to notify the user in a non-obstructing way, like so: (例如,填充数据的表或加载图像)一般的经验法则是以无障碍的方式通知用户,如下所示:

And then have a pull-to-refresh control or a refresh button.然后有一个下拉刷新控件或刷新按钮。

For background data updates that don't impact the user's actions or behavior:对于不影响用户操作或行为的后台数据更新:

You could easily add a retry counter into your request result depending on the code - but I'd be careful with this one and build out some more intelligent logic.您可以根据代码轻松地将重试计数器添加到您的请求结果中 - 但我会小心使用这个计数器并构建一些更智能的逻辑。 For example, given the following status codes, you might want to handle things differently:例如,给定以下状态代码,您可能希望以不同的方式处理事情:

  • 5xx: Something is wrong with your server. 5xx:您的服务器有问题。 You may want to delay the retry for 30s or a minute, but if it happens 3 or 4 times, you're going to want to stop hammering your back end.您可能希望将重试延迟 30 秒或一分钟,但如果发生 3 或 4 次,您将希望停止重试后端。

  • 401: The authenticated user may no longer be authorized to call your API. 401:经过身份验证的用户可能不再有权调用您的 API。 You're not going to want to retry this at all;你根本不想重试; instead, you'd probably want to log the user out so the next time they use your app they're prompted to re-authenticate.相反,您可能希望将用户注销,以便下次他们使用您的应用程序时,系统会提示他们重新进行身份验证。

  • Network time-out/lost connection: Retrying is irrelevant until connection is re-established.网络超时/丢失连接:在重新建立连接之前,重试无关紧要。 You could write some logic around your reachability handler to queue background requests for actioning the next time network connectivity is available.您可以围绕可达性处理程序编写一些逻辑,以便在下一次网络连接可用时对后台请求进行排队。

And finally, as we touched on in the comments, you might want to look at notification-driven background app refreshing.最后,正如我们在评论中提到的,您可能想要查看通知驱动的后台应用程序刷新。 This is where instead of polling your server for changes, you can send a notification to tell the app to update itself even when it's not running in the foreground.在这里,您可以发送通知来告诉应用程序进行自我更新,而不是轮询服务器以获取更改,即使它不在前台运行。 If you're clever enough, you can have your server repeat notifications to your app until the app has confirmed receipt - this solves for connectivity failures and a myriad of other server response error codes in a consistent way.如果您足够聪明,您可以让您的服务器向您的应用程序重复通知,直到应用程序确认收到 - 这以一致的方式解决了连接故障和无数其他服务器响应错误代码。

I'd categorize three methods for handling retry:我将处理重试的三种方法分类:

  1. Reachability Retry可达性重试
  • Reachability is a fancy way of saying "let me know when network connection has changed".可达性是一种说“当网络连接发生变化时让我知道”的奇特方式。 Apple has some snippets for this, but they aren't fun to look at — my recommendation is to use something like Ashley Mill's Reachability replacement. Apple 对此有一些片段,但它们看起来并不有趣——我的建议是使用类似 Ashley Mill 的Reachability替代品。
  • In addition to Reachability, Apple provides a waitsForConnectivity (iOS 11+) property that you can set on the URLSession configuration.除了可达性之外,Apple 还提供了一个waitsForConnectivity (iOS 11+) 属性,您可以在URLSession配置上设置该URLSession By setting it, you are alerted via the URLSessionDataDelegate when a task is waiting for a network connection.通过设置它,当任务正在等待网络连接时,您会通过URLSessionDataDelegate You could use that opportunity to enable an offline mode or display something to the user.您可以利用这个机会启用离线模式或向用户显示某些内容。
  1. Manual Retry手动重试
  • Let the user decide when to retry the request.让用户决定何时重试请求。 I'd say this is most commonly implemented using a "pull to refresh" gesture/UI.我会说这是最常见的使用“拉动刷新”手势/用户界面来实现的。
  1. Timed/Auto Retry定时/自动重试
  • Wait for a few second and try again.等待几秒钟,然后重试。
  • Apple's Combine framework provides a convenient way to retry failed network requests. Apple 的Combine 框架提供了一种重试失败的网络请求的便捷方法。 See Processing URL Session Data Task Results with Combine请参阅使用组合处理 URL 会话数据任务结果
  • From Apple Docs: Life Cycle of a URL Session (deprecated)... your app should not retry [a request] immediately, however.来自Apple Docs: Life Cycle of a URL Session (deprecated) ... 但是,您的应用不应立即重试 [a request]。 Instead, it should use reachability APIs to determine whether the server is reachable, and should make a new request only when it receives a notification that reachability has changed.相反,它应该使用可达性 API 来确定服务器是否可达,并且应该仅在收到可达性已更改的通知时才发出新请求。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM