SwiftHttp

一个很棒的 Swift HTTP 库,可以快速创建与 API 端点进行通信的层。

安装

您可以通过 Swift Package Manager 轻松地将 SwiftHttp 用作依赖项

.package(url: "https://github.com/binarybirds/swift-http", from: "1.0.0"),

swift-http 包中的 SwiftHttp 产品作为依赖项添加到您的目标中

.product(name: "SwiftHttp", package: "swift-http"),

导入框架

import SwiftHttp

就这样。

基本用法

使用 API 端点设置通信层非常容易。

import SwiftHttp

struct Todo: Codable {
    let id: Int
    let title: String
    let completed: Bool
}

struct TodoApi: HttpCodablePipelineCollection {

    let client: HttpClient = UrlSessionHttpClient(logLevel: .info)
    let apiBaseUrl = HttpUrl(host: "jsonplaceholder.typicode.com")

    
    func list() async throws -> [Todo] {
        try await decodableRequest(
            executor: client.dataTask,
            url: apiBaseUrl.path("todos"),
            method: .get
        )
    }    
}

// api usage
let todos = try await api.list()

// curl log
// curl "https://jsonplaceholder.typicode.com/todos/"

HttpClient 提供了执行 data、download 或 upload 任务的执行器。

当使用 codable 管道集合时,您可以创建可解码的、可编码的、codable 的或原始的请求。

使用原始请求 & 响应

您可以使用 HttpUrl 和 HttpRawRequest 类型创建原始 HTTP 请求。

let url = HttpUrl(
    scheme: "https",
    host: "jsonplaceholder.typicode.com",
    port: 80,
    path: ["todos"],
    resource: nil,
    query: [:],
    fragment: nil
)

let req = HttpRawRequest(
    url: url, 
    method: .get, 
    headers: [:], 
    body: nil
)

/// execute the request using the client
let client = UrlSessionHttpClient(session: .shared, logLevel: .info)
let response = try await client.dataTask(req)

/// use the response data
let todos = try JSONDecoder().decode([Todo].self, from: response.data)
// response.statusCode == .ok
// response.headers -> response headers as a dictionary

您可以使用特定的 HttpClient 来使用 data、download 或 upload 任务执行请求。

构造 URL

HttpUrl 类型允许我们使用基本 URL 构造各种 URL。

let baseUrl = HttpUrl(host: "jsonplaceholder.typicode.com")

// https://jsonplaceholder.typicode.com/todos/
baseUrl.path("todos")               

// https://jsonplaceholder.typicode.com/todos/1/
baseUrl.path("todos", String(1))    

// https://jsonplaceholder.typicode.com/todos/?foo=bar
baseUrl.path("todos").query("foo", "bar")

// https://jsonplaceholder.typicode.com/todos/?foo=baz&bar=1
baseUrl.path("todos").query([
    "foo": "baz",
    "bar": "1",
])

您可以使用 .url 属性将 HttpUrl 对象转换为 Foundation URL。

请求

您可以使用 HttpUrl 和 HttpMethod 创建原始请求对象,包括额外的 headers 和 body data。

let url = HttpUrl(host: "localhost", path: ["login"])

let token: String = "valid-token"
let body = try JSONEncoder().encode([
    "foo": "bar",
])
let req = HttpRawRequest(
    url: url,
    method: .post,
    headers: [
        .key(.authorization): "Bearer \(token)",
        .custom("my-header"): "my-header-value",
    ],
    body: body
)

/*
curl "https:///login/" \
    -X POST \
    -H 'my-header: my-header-value' \
    -H 'Authorization: Bearer valid-token' \
    -d '{"foo":"bar"}'
*/
print(req.urlRequest.curlString) 

您可以通过 .urlRequest 属性将 HttpRequest 转换为 URLRequest。您可以通过使用 URLRequest 对象上的 .curlString 属性来打印请求的 cURL 表示形式。

响应验证

您可以使用 HttpResponseValidator 对象来验证响应。

// mock response
let response = HttpRawResponse(
    statusCode: .ok,
    headers: [
        .key(.contentType): "application/json",
    ],
    data: .init()
)

// check if the status code is between 200 and 299               
let validator1 = HttpStatusCodeValidator() // -> (.ok), (.notFound), etc.
try validator1.validate(response)


// check if a header key exists and the value is equal to "application/json"
let validator2 = HttpHeaderValidator(.key(.contentType)) { value in
    value == "application/json"
}

try validator2.validate(response)

// validate using multiple validators
let validation = HttpResponseValidation([validator1, validator2])
try validation.validate(response)

可以使用 HttpResponseValidation 检查多个验证标准。

管道

管道允许您使用一组自定义操作来转换原始请求和原始响应。

您可以创建自己的 HttpRequestTransformer 对象,以将额外的 headers 添加到您的请求,并将自定义 body 对象编码为 data 值。

您可以创建自己的 HttpResponseTransformer 对象来验证响应并从响应数据中解码自定义值。

codable(encodable,decodable,codable)管道是这种方法的一个很好的例子。