虽然Swift 5.5的新并发系统在Xcode 13.2中开始向后兼容,但一些利用这些新并发功能的内置系统API仍然只在iOS 15、macOS Monterey和苹果2021年的其他操作系统上可用。
例如,如果我们试图在一个应用中使用新的async/await-favored版本的URLSession 数据任务API,而这个应用在早期操作系统版本中也是可用的,那么我们会得到一个编译器错误:
struct ModelLoader<Model: Decodable> {
var session = URLSession.shared
var decoder = JSONDecoder()
func loadModel(from url: URL) async throws -> Model {
// Error: 'data(from:delegate:)' is only available in iOS 15.0 or newer
let (data, _) = try await session.data(from: url)
let model = try decoder.decode(Model.self, from: data)
return model
}
}
值得庆幸的是,上述问题我们可以自己解决,因为Swift的新并发系统带有一个延续机制,可以让我们将现有的代码改造为支持async/await。
以下是我们如何使用该机制来复制上述支持async/await的URLSession API,以使其一直可以追溯到iOS 13:
@available(iOS, deprecated: 15.0, message: "Use the built-in API instead")
extension URLSession {
func data(from url: URL) async throws -> (Data, URLResponse) {
try await withCheckedThrowingContinuation { continuation in
let task = self.dataTask(with: url) { data, response, error in
guard let data = data, let response = response else {
let error = error ?? URLError(.badServerResponse)
return continuation.resume(throwing: error)
}
continuation.resume(returning: (data, response))
}
task.resume()
}
}
}
请注意我们是如何为上述扩展添加自定义的弃用注解的,这样,只要我们将应用的部署目标提高到iOS 15或以上,我们就会得到一个编译器警告。
有了上述措施,我们的ModelLoader 内的编译器错误现在已经消失了,因为我们现在能够使用async/await轻松地执行URLSession-based网络调用,即使是在一个向后兼容的代码库内。
还需要指出的是,上述那种工作并不是所有 async-标记的系统API都需要。事实上,那些从基于完成处理程序的Objective-C API自动翻译过来的API都是自动向后兼容的--就像并发功能本身,以及标准库类型如Task 和 AsyncStream.