swift-api-client 是一个全面且模块化的 Swift 库,用于 API 设计。

目录

库的主要目标

用法

该库的核心是 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

APIClient 是一个结构体,它结合了一个用于创建 URL 请求的闭包和一个配置的类型化字典 APIClient.Configs。 有两种主要方法可以扩展 APIClient

在客户端上执行操作涉及

所有内置扩展都使用这些修饰符。

内置的 APIClient 扩展

完整列表可在 文档 中找到。

请求构建

存在许多修改 URL 请求的方法,例如 querybodyheaderheadersmethodpathbody 等。

let client = APIClient(url: baseURL)
  .method(.post)
  .body(someEncodableBody)
  .query(someEncodableQuery)
  .header(.acceptEncoding, "UTF-8")

完整的修饰符列表可在 RequestModifiers.swift 中找到,所有这些都基于 modifyRequest 修饰符。

值得注意的非显式修饰符包括

请求执行

提供了方法 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 的简写

APIClientCaller

定义了具有多个内置 caller 的请求执行,用于各种请求类型,包括

所有内置 HTTP caller 都使用 .httpClient 配置,可以使用 .httpClient() 修饰符进行自定义。 默认的 .httpClientURLSession。 可以自定义当前的 .httpClient 实例,例如,使用自定义的 URLSession 配置或 async-http-client

可以为不同类型的请求创建自定义 caller,例如 WebSocket、GraphQL 等。

Serializer (序列化器)

Serializer 是一个结构体,用于描述响应序列化,具有多个内置的序列化器

.decodable 序列化器使用 .bodyDecoder 配置,可以使用 .bodyDecoder 修饰符进行自定义。 默认的 bodyDecoderJSONDecoder()

一些执行修饰符

编码和解码

有几个内置的配置用于编码和解码

这些编码器和解码器可以使用相应的修饰符进行自定义。

ContentSerializer (内容序列化器)

ContentSerializer 是一个结构体,用于描述请求体序列化,具有一个内置的内容序列化器:.encodable,它使用 .bodyEncoder 配置。 可以通过将 ContentSerializer 实例传递给 .body(_:as:) 修饰符来指定自定义内容序列化器。

Auth (鉴权)

可以使用 .auth(_:).auth(enabled:) 修饰符自定义 .auth.isAuthEnabled 配置,允许为所有请求注入身份验证类型,并为特定请求启用/禁用它。

.auth 配置是一个 AuthModifier 实例,具有几个内置的 AuthModifier 类型

Token refresher (令牌刷新器)

.tokenRefresher(...) 修饰符可用于指定令牌刷新闭包,该闭包在请求返回 401 状态代码时调用。 刷新闭包接收缓存的刷新令牌、客户端和响应,并返回一个新令牌,然后将其用于请求。 .refreshToken 还会设置 .auth 配置。

Mocking (模拟)

用于模拟请求的内置工具包括

此外,.mock(_:) 作为 APIClientCaller 提供了一种模拟请求的替代方法,例如 client.call(.mock(data), as: .decodable)

还可以创建和注入自定义 HTTPClient 实例以进行测试或预览。

Logging (日志)

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(_:) 修饰符自定义日志消息格式。

Metrics (指标)

swift-api-client 采用 swift-metrics 进行指标收集,.reportMetrics 配置可以通过 .reportMetrics(_:) 修饰符进行自定义。

swift-api-client 报告

APIClient.Configs (配置)

配置值的集合通过修饰符链传播。 这些配置可以在所有核心方法中访问:modifyRequestwithRequestwithConfigs

要创建自定义配置值,请使用新属性扩展 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 声明。

/// /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 和 URLComponents 的便捷方法,提供了一个流畅的接口,用于设置路径组件、查询参数和其他 URL 组件。

let url = URL(string: "https://example.com")!
    .path("path1", "path2")
    .query("key1", 1)

这些扩展简化和精简了 Swift 中的 URL 构建和修改过程。

介绍 swift-api-client-addons

为了增强你使用 swift-api-client 的体验,我很高兴介绍 swift-api-client-addons —— 一个补充库,旨在通过附加功能和实用程序扩展 swift-api-client 的核心功能。

安装

  1. Swift Package Manager (Swift 包管理器)

创建一个 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 贡献代码! 请阅读我们的贡献指南以开始。