Swift中的Web API客户端 | kean.blog

作者:API传播员 · 2026-01-12 · 阅读时间:5分钟

自从我在 Swift 的上一篇 API 客户端文章发布以来,已经过去了四年多。在这段时间里,Swift 发生了许多变化,尤其是引入了 Async/Await 和 Actors,使得在 Swift 中设计自定义 Web API 客户端变得更加简单和有趣。本文将以 GitHub API 为例,探讨如何利用这些新特性设计一个高效的 API 客户端。


实现 API 客户端

实现一个 API 客户端的第一步是定义一个表示请求的类型。这个类型可以根据需求变得非常复杂,但以下是一个良好的起点:

struct Request {
    let path: String
    let method: String
    let headers: [String: String]
    let body: Data?
}

接下来,需要一个客户端来执行这些请求。这个客户端是基于 URLSession 的轻量封装,便于修改和扩展。以下是一个简单的实现:

class APIClient {
    private let session: URLSession
    private let baseURL: URL

    init(baseURL: URL, session: URLSession = .shared) {
        self.baseURL = baseURL
        self.session = session
    }    func send(_ request: Request, responseType: T.Type) async throws -> T {
        let urlRequest = try makeRequest(from: request)
        let (data, _) = try await session.data(for: urlRequest)
        return try JSONDecoder().decode(T.self, from: data)
    }    private func makeRequest(from request: Request) throws -> URLRequest {
        var components = URLComponents(url: baseURL.appendingPathComponent(request.path), resolvingAgainstBaseURL: false)
        components?.queryItems = request.queryItems
        guard let url = components?.url else {
            throw URLError(.badURL)
        }
        var urlRequest = URLRequest(url: url)
        urlRequest.httpMethod = request.method
        urlRequest.allHTTPHeaderFields = request.headers
        urlRequest.httpBody = request.body
        return urlRequest
    }
}

这个基础的 APIClient 已经能够满足大多数需求,同时也为进一步扩展提供了灵活性。


扩展 API 客户端功能

虽然基础功能容易实现,但更高级的用例需要额外的处理。以下是一些常见的扩展场景:

用户授权

在需要用户授权的场景中,可以通过 async/await 实现令牌刷新逻辑。例如:

func client(_ client: APIClient, willSendRequest request: inout URLRequest) async throws {
    if let token = await refreshTokenIfNeeded() {
        request.setValue("Bearer (token)", forHTTPHeaderField: "Authorization")
    }
}

相比传统的回调方式,async/await 的实现更加简洁且不易出错。

双向 TLS(mTLS)

在双向 TLS 中,客户端需要向服务器提供证书。URLSession 原生支持 mTLS,以下是一个简单的实现:

func configureSession() -> URLSession {
    let configuration = URLSessionConfiguration.default
    configuration.tlsMinimumSupportedProtocol = .tlsProtocol12
    return URLSession(configuration: configuration, delegate: self, delegateQueue: nil)
}

主要挑战在于如何安全地管理私钥,例如通过嵌入模糊化的 .p12 文件。

HTTP 缓存

HTTP 缓存是一个灵活的系统,允许服务器和客户端共同决定缓存策略URLSessionURLCache 提供了开箱即用的支持:

let request = URLRequest(url: url, cachePolicy: .returnCacheDataElseLoad, timeoutInterval: 60)

通过调整 CachePolicy,可以灵活控制缓存行为。


定义 API

对于较小的应用,可以直接使用 APIClient 发起请求。但在更复杂的场景中,定义 API 是一个更好的选择。例如,可以为每个资源创建一个单独的类型,并在其上定义可用的 HTTP 方法

struct UserAPI {
    let client: APIClient

    func getUserDetails(userID: String) async throws -> User {
        let request = Request(path: "/users/(userID)", method: "GET", headers: [:], body: nil)
        return try await client.send(request, responseType: User.self)
    }
}

这种方式虽然稍显繁琐,但可以减少代码重复并提高可维护性。


现代工具的支持

现代工具如 Pulse 提供了强大的日志记录功能,可以无缝集成APIClient 中:

Pulse 日志记录

通过简单的设置,即可收集详尽的网络请求信息,帮助开发者快速定位问题。


总结

借助 Swift 的 Async/Await 和 Actors 特性,构建自定义 Web API 客户端变得更加高效和直观。无论是基础功能还是高级用例,这些新特性都提供了极大的灵活性和便利性。通过合理设计和扩展,开发者可以轻松应对各种复杂的 API 场景。

原文链接: https://kean.blog/post/new-api-client