Combine 系列
- Swift Combine 从入门到精通一
- Swift Combine 发布者订阅者操作者 从入门到精通二
- Swift Combine 管道 从入门到精通三
- Swift Combine 发布者publisher的生命周期 从入门到精通四
- Swift Combine 操作符operations和Subjects发布者的生命周期 从入门到精通五
- Swift Combine 订阅者Subscriber的生命周期 从入门到精通六
- Swift 使用 Combine 进行开发 从入门到精通七
- Swift 使用 Combine 管道和线程进行开发 从入门到精通八
- Swift Combine 使用 sink, assign 创建一个订阅者 从入门到精通九
- Swift Combine 使用 dataTaskPublisher 发起网络请求 从入门到精通十
- Swift Combine 用 Future 来封装异步请求 从入门到精通十一
- Swift Combine 有序的异步操作 从入门到精通十二
- Swift Combine 使用 flatMap 和 catch错误处理 从入门到精通十三
1. 使用 flatMap 和 catch 在不取消管道的情况下处理错误
目的: flatMap
操作符可以与 catch
一起使用,以持续处理新发布的值上的错误。
flatMap 是用于处理持续事件流中错误的操作符。
你提供一个闭包给 flatMap
,该闭包可以获取所传入的值,并创建一个一次性的发布者,完成可能失败的工作。 这方面的一个例子是从网络请求数据,然后将其解码。 你可以引入一个 catch
操作符,以捕获任何错误并提供适当的值。
当你想要保持对上游发布者的更新时,这是一个完美的机制,因为它创建一次性的发布者或短管道,发送一个单一的值,然后完成每一个传入的值。 所创建的一次性发布者的完成事件在 flatMap
中终止,并且不会传递给下游订阅者。
一个使用 dataTaskPublisher
的这样的例子:
let remoteDataPublisher = Just(self.testURL!) // 1
.flatMap { url in // 2
URLSession.shared.dataTaskPublisher(for: url) // 3
.tryMap { data, response -> Data in // 4
guard let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode == 200 else {
throw TestFailureCondition.invalidServerResponse
}
return data
}
.decode(type: PostmanEchoTimeStampCheckResponse.self, decoder: JSONDecoder()) // 5
.catch {_ in // 6
return Just(PostmanEchoTimeStampCheckResponse(valid: false))
}
}
.eraseToAnyPublisher()
Just
以传入一个URL
作为示例启动此发布者。flatMap
以 URL 作为输入,闭包继续创建一次性发布者管道。dataTaskPublisher
使用输入的 url 发出请求。- 输出的结果(一个
(Data, URLResponse)
元组)流入tryMap
以解析其他错误。 decode
尝试将返回的数据转换为本地定义的类型。- 如果其中任何一个失败,
catch
将把错误转换为一个默认的值。 在这个例子中,是具有预设好valid = false
属性的对象。
2. 网络受限时从备用 URL 请求数据
目的: 在 Apple 的 WWDC 2019 演示 Advances in Networking, Part 1 中,使用 tryCatch
和 tryMap
操作符提供了示例模式,以响应网络受到限制的特殊错误。
// Generalized Publisher for Adaptive URL Loading
func adaptiveLoader(regularURL: URL, lowDataURL: URL) -> AnyPublisher<Data, Error> {
var request = URLRequest(url: regularURL) // 1
request.allowsConstrainedNetworkAccess = false // 2
return URLSession.shared.dataTaskPublisher(for: request) // 3
.tryCatch { error -> URLSession.DataTaskPublisher in // 4
guard error.networkUnavailableReason == .constrained else {
throw error
}
return URLSession.shared.dataTaskPublisher(for: lowDataURL) // 5
.tryMap { data, response -> Data in
guard let httpResponse = response as? HTTPUrlResponse, // 6
httpResponse.statusCode == 200 else {
throw MyNetworkingError.invalidServerResponse
}
return data
}
.eraseToAnyPublisher() // 7
在苹果的 WWDC 中的这个例子,提供了一个函数,接受两个 URL 作为参数 —— 一个主要的 URL 和一个备用的。 它会返回一个发布者,该发布者将请求数据,并在网络受到限制时向备用 URL 请求数据。
request
变量是一个尝试请求数据的URLRequest
。- 设置
request.allowsConstrainedNetworkAccess
将导致dataTaskPublisher
在网络受限时返回错误。 - 调用
dataTaskPublisher
发起请求。 tryCatch
用于捕获当前的错误状态并检查特定错误(受限的网络)。- 如果它发现错误,它会使用备用
URL
创建一个新的一次性发布者。 - 由此产生的发布者仍可能失败,
tryMap
可以基于对应到错误条件的HTTP
响应码来抛出错误,将此映射为失败。 eraseToAnyPublisher
可在操作符链上进行类型擦除,因此adaptiveLoader
函数的返回类型为AnyPublisher<Data, Error>
。
在示例中,如果从原始请求返回的错误不是网络受限的问题,则它会将 .failure
结束事件传到管道中。 如果错误是网络受限,则 tryCatch
操作符会创建对备用 URL
的新请求。
参考
https://heckj.github.io/swiftui-notes/index_zh-CN.html
代码
https://github.com/heckj/swiftui-notes