褪黑素
褪黑素(曾是 SwiftyNetworking)库是一个 Swift 编写的网络库,它提供了一种基于协议的方法来加载数据请求。它提供了一个 Endpoint
协议来确保网络请求以通用和类型安全的方式解析。早期更改可以在不再更新的 SwiftyNetworking 库中追踪。
Endpoint 协议
遵循 Endpoint
协议很简单且直接。这是协议体的样子
public protocol Endpoint {
associatedtype Response
var scheme: Scheme { get }
var host: String { get }
var port: Int? { get }
var path: String { get }
var method: HTTPMethod { get }
var queries: [URLQuery] { get }
var headers: [HTTPHeader] { get }
func prepare(request: inout URLRequest)
func parse(data: Data, urlResponse: URLResponse) throws -> Response
}
该库包含了一些变量和函数的默认实现,以提高便利性。
构建 URLRequest
任何遵循 Endpoint
的对象将自动获取 url
和 request
属性,这将被 URLSession
用于加载请求。
如果您需要修改在加载之前请求,可以实现 prepare(request:) 方法。
@Query 属性包装器
@Query
属性包装器用于声明任何作为 URL 查询的属性。您端点的体内使用 @Query
声明的所有属性都将添加到最终的 URL 中。
struct APIEndpoint: Endpoint {
...
@Query(name: "name") var name: String? = "the-braveknight"
@Query(name: "age") var pageNumber: Int? = 2
...
}
在上面的代码中,URL 查询将如下所示: ?name=the-braveknight&pageSize=2
。您仍然可以通过直接设置端点中的 queries
属性来添加多个查询。
@Header 属性包装器
同样,@Header
属性包装器 用于声明头部,这将在加载之前添加到 URLRequest
。库包含多个常用 HTTP 头部,您也可以实现自己的。
struct APIEndpoint: Endpoint {
...
@Header(\.accept) var accept: MIMEType = .json
@Header(\.contentType) var contentType: MIMEType = .json
...
}
同样,您一次仍然可以添加多个头部,直接设置端点中的 headers
属性。
@QueryGroup & @HeaderGroup
这些结果构建器允许您分别构建查询项和头部的数组。例如,我们可以使用它们来构建端点的 queries
和 headers
数组。
struct APIEndpoint: Endpoint {
...
@HeaderGroup var headers: [HTTPHeader] {
Accept(.json)
ContentType(.json)
}
@QueryGroup var queries: [URLQuery] {
Query(name: "name", value: "the-braveknight")
Query(name: "age", value: 2)
}
...
}
解码响应
在某些情况下,例如当 Response
遵循 Decodable
且我们期望解码 JSON 时,为 parse(data:urlResponse:) 方法提供默认实现来处理该操作是合理的。
public extension Endpoint where Response : Decodable {
func parse(data: Data, urlResponse: URLResponse) throws -> Response {
let decoder = JSONDecoder()
return try decoder.decode(Response.self, from: data)
}
}
您仍然可以提供此方法的自己的实现来覆盖此实现。
一个示例端点
这是一个使用 GET
方法的示例端点,用于解析来自 Agify.io API 的请求。
API 调用响应体(https://api.agify.io/?name=bella)如下所示
{
"name" : "bella",
"age" : 34,
"count" : 40138
}
可以包含此数据的自定义 Swift 结构如下所示
struct Person : Decodable {
let name: String
let age: Int
}
最后,这是我们的端点看起来像什么样式
struct AgifyAPIEndpoint : Endpoint {
typealias Response = Person
let host: String = "api.agify.io"
let path: String = "/"
@Query(name: "name") var name: String? = nil
@Header(\.accept) var accept: MIMEType = .json
}
我们可以使用 Swift 的点语法来使其调用端点更加方便。
extension Endpoint where Self == AgifyAPIEndpoint {
static func estimatedAge(forName personName: String) -> Self {
AgifyAPIEndpoint(name: personName)
}
}
最后,这是我们调用端点的方式。结果是 Result<Person, Error>
类型。
URLSession.shared.load(.estimatedAge(forName: "Zaid")) { result in
do {
let person = try result.get()
print("\(person.name) is probably \(person.age) years old.")
} catch {
// Handle errors
}
}
Combine
褪黑素支持使用 Combine
框架加载数据。
let subscription: AnyCancellable = URLSession.shared.load(.estimatedAge(forName: "Zaid"))
.sink { completion in
// Handle errors
} receiveValue: { person in
print("\(person.name) is probably \(person.age) years old.")
}
Swift 并发
美拉酮还支持使用 Swift Concurrency 和 async/await
来加载端点。
Task {
do {
let person = try await URLSession.shared.load(.estimatedAge(forName: "Zaid"))
print("\(person.name) is probably \(person.age) years old.")
} catch {
// Handle errors
}
}
致谢
- 感谢来自 SwiftBySundell 的灵感。