swift-api-client
是一个全面且模块化的 Swift 库,用于 API 设计。
APIClient
APIClient
扩展
APIClient.Configs (配置)
swift-api-client-addons
该库的核心是 APIClient
结构体,既充当请求构建器又充当执行器。 它是一个泛型结构体,支持用于与 URL 请求相关的任何任务。
APIClient 的分支和配置注入/覆盖功能,扩展到其所有子实例,有助于轻松地重用网络逻辑和任务,从而消除了复制粘贴的需要。
虽然 Example folder (示例文件夹) 中提供了一个完整的示例,但这是一个简单的用法示例
let client = APIClient(url: baseURL)
.bodyDecoder(.json(dateDecodingStrategy: .iso8601))
.bodyEncoder(.json(dateEncodingStrategy: .iso8601))
.errorDecoder(.decodable(APIError.self))
.tokenRefresher { refreshToken, client, _ in
guard let refreshToken else { throw APIError.noRefreshToken }
let tokens: AuthTokens = try await client("auth", "token")
.body(["refresh_token": refreshToken])
.post()
return (tokens.accessToken, tokens.refreshToken, tokens.expiresIn)
} auth: {
.bearer(token: $0)
}
// Create a `APIClient` instance for the /users path
let usersClient = client("users")
// GET /users?name=John&limit=1
let john: User = try await usersClient
.query(["name": "John", "limit": 1])
.auth(enabled: false)
.get()
// Create a `APIClient` instance for /users/{userID} path
let johnClient = usersClient(john.id)
// GET /user/{userID}
let user: User = try await johnClient.get()
// PUT /user/{userID}
try await johnClient.body(updatedUser).put()
// DELETE /user/{userID}
try await johnClient.delete()
此外,你可以使用宏进行 API 声明
/// /pet
@Path
struct Pet {
/// PUT /pet
@PUT("/") public func update(_ body: PetModel) -> PetModel {}
/// POST /pet
@POST("/") public func add(_ body: PetModel) -> PetModel {}
/// GET /pet/findByStatus
@GET public func findByStatus(@Query _ status: PetStatus) -> [PetModel] {}
/// GET /pet/findByTags
@GET public func findByTags(@Query _ tags: [String]) -> [PetModel] {}
}
APIClient
是一个结构体,它结合了一个用于创建 URL 请求的闭包和一个配置的类型化字典 APIClient.Configs
。 有两种主要方法可以扩展 APIClient
modifyRequest
修饰符。configs
修饰符。在客户端上执行操作涉及
withRequest
方法。所有内置扩展都使用这些修饰符。
完整列表可在 文档 中找到。
存在许多修改 URL 请求的方法,例如 query
、body
、header
、headers
、method
、path
、body
等。
let client = APIClient(url: baseURL)
.method(.post)
.body(someEncodableBody)
.query(someEncodableQuery)
.header(.acceptEncoding, "UTF-8")
完整的修饰符列表可在 RequestModifiers.swift 中找到,所有这些都基于 modifyRequest
修饰符。
值得注意的非显式修饰符包括
.callAsFunction(path...)
- 作为 .path(path...)
修饰符的简写,允许 client("path")
而不是 client.path("path")
。.get
、.post
、.put
、.delete
、.patch
。提供了方法 call(_ caller: APIClientCaller<...>, as serializer: Serializer<...>)
。 示例
try await client.call(.http, as: .decodable)
try await client.call(.http, as: .void)
try client.call(.httpPublisher, as: .decodable)
还有内置 caller 和 serializer 的简写
call()
等价于 call(.http, as: .decodable)
或 call(.http, as: .void)
callAsFunction()
的作用类似于 call()
,将 client.delete()
简化为 client.delete.call()
或 client()
而不是 client.call()
等。定义了具有多个内置 caller 的请求执行,用于各种请求类型,包括
.http
用于使用 try await
语法的 HTTP 请求。.httpPublisher
用于使用 Combine 语法的 HTTP 请求。.httpDownload
用于使用 try await
语法的 HTTP 下载请求。.mock
用于使用 try await
语法的模拟请求。所有内置 HTTP caller 都使用 .httpClient
配置,可以使用 .httpClient()
修饰符进行自定义。 默认的 .httpClient
是 URLSession
。 可以自定义当前的 .httpClient
实例,例如,使用自定义的 URLSession
配置或 async-http-client。
可以为不同类型的请求创建自定义 caller,例如 WebSocket、GraphQL 等。
Serializer
是一个结构体,用于描述响应序列化,具有多个内置的序列化器
.decodable
用于将响应解码为 Decodable 类型。.data
用于获取原始 Data 响应。.void
用于忽略响应。.instance
用于接收与 APIClientCaller
返回类型相同的响应。 对于 HTTP 请求,它是 Data
。.decodable
序列化器使用 .bodyDecoder
配置,可以使用 .bodyDecoder
修饰符进行自定义。 默认的 bodyDecoder
是 JSONDecoder()
。
.retry(limit:)
用于重试指定次数的请求。.throttle(interval:)
用于使用指定的间隔来限制请求。.timeout(_:)
用于设置执行超时。.waitForConnection()
用于在执行请求之前等待连接。.backgroundTask()
用于在后台任务中执行请求。.retryIfFailedInBackground()
用于在后台失败时重试请求。有几个内置的配置用于编码和解码
.bodyEncoder
用于编码请求体。 内置的编码器包括 .json
、.formURL
和 .multipartFormData
。.bodyDecoder
用于解码请求体。 内置的解码器是 .json
。.queryEncoder
用于编码查询。 内置的编码器是 .query
。.errorDecoder
用于解码错误响应。 内置的解码器是 .decodable(type)
。这些编码器和解码器可以使用相应的修饰符进行自定义。
ContentSerializer
是一个结构体,用于描述请求体序列化,具有一个内置的内容序列化器:.encodable
,它使用 .bodyEncoder
配置。 可以通过将 ContentSerializer
实例传递给 .body(_:as:)
修饰符来指定自定义内容序列化器。
可以使用 .auth(_:)
和 .auth(enabled:)
修饰符自定义 .auth
和 .isAuthEnabled
配置,允许为所有请求注入身份验证类型,并为特定请求启用/禁用它。
.auth
配置是一个 AuthModifier
实例,具有几个内置的 AuthModifier
类型
.bearer(token:)
用于 Bearer token 身份验证。.basic(username:password:)
用于 Basic 身份验证。.apiKey(key:field:)
用于 API Key 身份验证。.tokenRefresher(...)
修饰符可用于指定令牌刷新闭包,该闭包在请求返回 401 状态代码时调用。 刷新闭包接收缓存的刷新令牌、客户端和响应,并返回一个新令牌,然后将其用于请求。 .refreshToken
还会设置 .auth
配置。
用于模拟请求的内置工具包括
.mock(_:)
修饰符,用于为请求指定模拟响应。Mockable
协议允许模拟任何返回 Mockable
响应的请求,即使没有 .mock(_:)
修饰符。.usingMocksPolicy
配置定义是否使用模拟,可以使用 .usingMocks(policy:)
修饰符进行自定义。 默认情况下,模拟在 live
环境中被忽略,并按指定用于测试和 SwiftUI 预览。此外,.mock(_:)
作为 APIClientCaller
提供了一种模拟请求的替代方法,例如 client.call(.mock(data), as: .decodable)
。
还可以创建和注入自定义 HTTPClient 实例以进行测试或预览。
swift-api-client
采用 swift-log
进行日志记录,.logger
和 .logLevel
配置可通过 logger
和 .log(level:)
修饰符进行自定义。 默认日志级别为 .info
。 提供了一个内置的 .none
Logger 来禁用所有日志。
日志示例
[29CDD5AE-1A5D-4135-B76E-52A8973985E4] ModuleName/FileName.swift/72
--> 🌐 PUT /petstore (9-byte body)
Content-Type: application/json
--> END PUT
[29CDD5AE-1A5D-4135-B76E-52A8973985E4]
<-- ✅ 200 OK (100ms, 15-byte body)
可以使用 .loggingComponents(_:)
修饰符自定义日志消息格式。
swift-api-client
采用 swift-metrics
进行指标收集,.reportMetrics
配置可以通过 .reportMetrics(_:)
修饰符进行自定义。
swift-api-client
报告
api_client_requests_total
: 请求总数。api_client_responses_total
: 响应总数。api_client_errors_total
: 错误总数。http_client_request_duration_seconds
: http 请求持续时间。配置值的集合通过修饰符链传播。 这些配置可以在所有核心方法中访问:modifyRequest
、withRequest
和 withConfigs
。
要创建自定义配置值,请使用新属性扩展 APIClient.Configs
结构。 使用带有你的属性键路径的下标来获取和设置该值,并为客户端提供一个专用的修饰符,以便在设置该值时使用
extension APIClient.Configs {
var myCustomValue: MyConfig {
get {
self[\.myCustomValue] ?? myDefaultConfig
}
set {
self[\.myCustomValue] = newValue
}
}
}
extension APIClient {
func myCustomValue(_ myCustomValue: MyConfig) -> APIClient {
configs(\.myCustomValue, myCustomValue)
}
}
有一个 valueFor
全局方法,允许你根据环境定义默认值:live、test 或 preview。
所有配置都收集在最终的 withRequest
方法中,然后传递给所有修饰符,因此使用最后定义的值。 请注意,所有执行方法(例如 call
)都基于 withRequest
方法。
例如,以下代码在所有情况下都会打印 3
let configs = try client
.configs(\.intValue, 1)
.modifyRequest { _, configs in
print(configs.intValue) // 3
}
.configs(\.intValue, 2)
.modifyRequest { _, configs in
print(configs.intValue) // 3
}
.configs(\.intValue, 3)
.withRequest { _, configs in
print(configs.intValue) // 3
return configs
}
print(configs.intValue) // 3
swift-api-client
提供了一组宏,以便更轻松地进行 API 声明。
API
宏。Path
宏。Cal(_ method:)
、GET
、POST
、PUT
等宏。 示例/// /pet
@Path
struct Pet {
/// PUT /pet
@PUT("/") public func update(_ body: PetModel) -> PetModel {}
/// POST /pet
@POST("/") public func add(_ body: PetModel) -> PetModel {}
/// GET /pet/findByStatus
@GET public func findByStatus(@Query _ status: PetStatus) -> [PetModel] {}
/// GET /pet/findByTags
@GET public func findByTags(@Query _ tags: [String]) -> [PetModel] {}
/// /pet/{id}
@Path("{id}")
public struct PetByID {
/// GET /pet/{id}
@GET("/")
func get() -> PetModel {}
/// DELETE /pet/{id}
@DELETE("/")
func delete() {}
/// POST /pet/{id}
@POST("/") public func update(@Query name: String?, @Query status: PetStatus?) -> PetModel {}
/// POST /pet/{id}/uploadImage
@POST public func uploadImage(_ body: Data, @Query additionalMetadata: String? = nil) {}
}
}
使用 swift-api-client
不需要宏;它们只是语法糖。
当然,这是一个简洁的部分,用于你的 README,它演示了你的扩展提供的最方便的方法
这些扩展提供了用于配置 URL 和 URLComponents 的便捷方法,提供了一个流畅的接口,用于设置路径组件、查询参数和其他 URL 组件。
let url = URL(string: "https://example.com")!
.path("path1", "path2")
.query("key1", 1)
这些扩展简化和精简了 Swift 中的 URL 构建和修改过程。
为了增强你使用 swift-api-client
的体验,我很高兴介绍 swift-api-client-addons
—— 一个补充库,旨在通过附加功能和实用程序扩展 swift-api-client
的核心功能。
创建一个 Package.swift
文件。
// swift-tools-version:5.7
import PackageDescription
let package = Package(
name: "SomeProject",
dependencies: [
.package(url: "https://github.com/dankinsoid/swift-api-client.git", from: "1.7.10")
],
targets: [
.target(
name: "SomeProject",
dependencies: [
.product(name: "SwiftAPIClient", package: "swift-api-client"),
]
)
]
)
$ swift build
Daniil Voidilov, voidilov@gmail.com
swift-api-client 基于 MIT 许可证发布。 详情请参阅 LICENSE 文件。
欢迎您为 Swift-Networking 贡献代码! 请阅读我们的贡献指南以开始。