在阅读 Apple Education 出版的 Develop in Swift Data Collections 这本书时,关于 URLSession 部分,注意到在 Swift 5.5 之后引入新的 async / await API,因此想简单做个笔记。而本文主要目的是比较新旧不同的实作方式,对于错误处理就先简单处理。如果有理解错误,还烦请指正,谢谢。内容主要改编书中提到的例子。
先定义取得的资料 Structenum PhotoInfoError: Error, LocalizedError { case itemNotFound}struct PhotoInfo: Codable { var title: String var description: String var url: URL var copyright: String? enum CodingKeys: String, CodingKey { case title case description = "explanation" case url case copyright }}
使用 async / awaitfunc fetchPhotoInfoNew() async throws -> PhotoInfo { let url = URL(string: "https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY")! let session = URLSession(configuration: .default) let (data, response) = try await session.data(from: url) guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else { throw PhotoInfoError.itemNotFound } let jsonDecoder = JSONDecoder() let photoInfo = try jsonDecoder.decode(PhotoInfo.self, from: data) return(photoInfo)}Task { do { let photoInfo = try await fetchPhotoInfoNew() print("Successfully fetched PhotoInfo: \(photoInfo)") } catch { print("Fetch PhotoInfo failed with error: \(error)") }}
用 async 关键字宣告 fetchPhotoInfoNew()
为非同步函式,并使用 await 关键字执行 URLSession 的 data(from:)
方法获取 URL 的回应。当回应的 HTTP 状态码为 200 时,会将数据解码为 PhotoInfo 并返回该实例。Task 这个实例则用于执行非同步函数 fetchPhotoInfoNew()
,印出 photoInfo 的值或处理错误。
而在这之前的作法为使用 URLSession.dataTask(with: URLRequest, completionHandler: (Data?, URLResponse?, Error?) -> Void) { ... }
当 data task 完成时,在 completionHandler
这个 closure 里处理 response、data,或者 error 的状况。也就是说,这个 closure 是当 task 完成时会被自动呼叫,并透过这个 closure 回传结果,以便开发者进一步处理。程式码如下:
URLSession.dataTask
func fetchPhotoInfoOld() { if let url = URL(string: "https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY") { let session = URLSession(configuration: .default) let task = session.dataTask(with: url) { data, response, error in guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else { return } guard let safeData = data else { print("No data") return } do { let jsonDecoder = JSONDecoder() let photoInfo = try jsonDecoder.decode(PhotoInfo.self, from: safeData) print("Successfully fetched PhotoInfo: \(photoInfo)") } catch { print("Failed to decode PhotoInfo: \(error)") } } task.resume() } }fetchPhotoInfoOld()
使用 async / await 的好处是避免使用那一串 closure,让程式码变得较为简洁。