Swift6之后更现代的REST API方法

2.2k 词

目前网上检索到的关于Swift的REST API Call实现很多已经在swift6之后算是过时了的,Swift6强化了并发安全,以及URLsession API也相应增加了async版本(旧版本使用callback),更modern的方法是使用async/await以及Task来构建REST API Call

以下均使用URLsession进行REST API Call

在旧版Swift中, Get接口是这么call的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
    func fetchPage(completion: @escaping (Result<PageData, Error>) -> Void) {

let url = URL(string: "http://127.0.0.1:8080/community/page/get")!

URLSession.shared.dataTask(with: url) { data, response, error in

if let error = error {
completion(.failure(error))
return
}

guard let data = data else {
completion(.failure(URLError(.badServerResponse)))
return
}

do {
let result = try JSONDecoder().decode(PageData.self, from: data)
completion(.success(result))
} catch {
completion(.failure(error))
}

}.resume()
}

大量使用了回调函数。
另外值得说的是在Swift6中xcode默认会给没有显式隔离的代码视作@MainActor,也就是默认跑主线程,所以在老式写法里对于let result = try JSONDecoder().decode(PageData.self, from: data)这里要在PageData的结构体上标注一个nonisolated

Swift6以后,新式Get Call 的async/await写法是这样的

1
2
3
4
5
func fetchPage() async throws -> PageData {
let url = URL(string: "http://127.0.0.1:8080/community/page/get")!
let (data, _) = try await URLSession.shared.data(from: url)
return try JSONDecoder().decode(PageData.self, from: data)
}

wrap的时候可以通过Task来执行它

1
2
3
4
5
6
7
8
func load() {
Task{
let newPageData = try await fetchPage()
···
······
·········
}
}

同样的这是Swift6之后现代版的Post Call

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
func createTopic(title: String, content: String, time: Int64) async throws -> Topic {

let newTopic = Topic(
id: nil,
title: title,
content: content,
create_time: time
)

guard let url = URL(string: "http://127.0.0.1:8080/community/page/addtopic") else {
throw URLError(.badURL)
}

var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")

request.httpBody = try JSONEncoder().encode(newTopic)

let (data, response) = try await URLSession.shared.data(for: request)

guard let httpResponse = response as? HTTPURLResponse,
200..<300 ~= httpResponse.statusCode else {
throw URLError(.badServerResponse)
}

return try JSONDecoder().decode(Topic.self, from: data)
}

wrap之后用Task执行也是和get同理

留言