使用Combine升级您的Swift API客户端 - Andrea Scuderi

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

使用Combine Swift API客户端,并回答以下关键问题:

  • 如何使用Combine获取API响应?
  • 旧API客户端与使用Combine的新客户端有何不同?
  • 使用Combine从API检索数据有哪些优势?
  • 如何升级旧API以支持Combine?
  • 如何使用Combine管理嵌套的API调用?

使用Vapor和Docker演示API

在开始之前,请确保您已安装Vapor和Docker。如果尚未安装,可以参考相关文档进行安装。完成后,在Mac终端中运行以下命令以创建一个示例API项目:

vapor new demo-api --template=auth-template

您可以尝试使用vapor run运行该应用程序,但需要注意,macOS 15测试版和Xcode 11测试版的运行可能不够稳定。

接下来,进入项目目录并构建Docker镜像:

cd demo-api
cp web.Dockerfile Dockerfile
docker build . -t demo-api:latest

使用以下命令启动监听8080端口的REST API服务:

docker run -p 8080:80 demo-api:latest

打开一个新的终端窗口,检查API是否正常运行:

curl http://localhost:8080

如果一切正常,您将看到类似以下的输出:

_p17

旧API客户端的实现

首先,使用Xcode 11(目前为beta 4)创建一个新的空白Playground,并复制以下代码:

import Foundation

let url = URL(string: "https://jsonplaceholder.typicode.com/users/2")!
let task = URLSession.shared.dataTask(with: url) { data, response, error in
    guard let data = data else { return }
    print(String(data: data, encoding: .utf8)!)
}
task.resume()

运行Playground,检查是否成功获取了用户数据。旧客户端通过dataTask函数实现,虽然简单易用,但在处理嵌套调用时代码会变得难以维护。


使用Combine获取API响应

通过Combine框架,可以使用dataTaskPublisher实现API调用。以下是示例代码:

import Combine
import Foundation

let url = URL(string: "https://jsonplaceholder.typicode.com/users/2")!
let publisher = URLSession.shared.dataTaskPublisher(for: url)
    .map(.data)
    .decode(type: User.self, decoder: JSONDecoder())
    .sink(receiveCompletion: { completion in
        switch completion {
        case .finished:
            print("Finished")
        case .failure(let error):
            print("Error: (error)")
        }
    }, receiveValue: { user in
        print("User: (user)")
    })

在这里,dataTaskPublisher返回一个发布者,您需要通过sink订阅以接收数据。


解码有效负载时的差异

一个优秀的API客户端需要将接收到的原始数据映射到内部的classstruct。通过Swift的Codable协议,可以轻松地将JSON数据解码为模型对象。

旧客户端(dataTask

旧客户端虽然易于实现,但在处理嵌套调用时,代码的可读性会显著下降。

新客户端(dataTaskPublisher

新客户端的代码稍显复杂,但通过Combine的管道式处理,嵌套调用变得更加清晰易读。


升级旧API以支持Combine

通过使用Combine的Future,可以将旧的dataTask转换为发布者。以下是示例代码:

import Combine
import Foundation

func fetchData(url: URL) -> Future {
    return Future { promise in
        URLSession.shared.dataTask(with: url) { data, _, error in
            if let error = error {
                promise(.failure(error))
            } else if let data = data {
                promise(.success(data))
            }
        }.resume()
    }
}

通过这种方式,您可以逐步将旧API迁移到Combine架构。


带Combine的完整API客户端定义

以下是一个完整的API客户端示例,使用dataTaskPublisher实现:

import Combine
import Foundation

struct User: Codable {
    let id: Int
    let name: String
}func fetchUser(url: URL) -> AnyPublisher {
    return URLSession.shared.dataTaskPublisher(for: url)
        .tryMap { output in
            guard let response = output.response as? HTTPURLResponse, response.statusCode == 200 else {
                throw URLError(.badServerResponse)
            }
            return output.data
        }
        .decode(type: User.self, decoder: JSONDecoder())
        .eraseToAnyPublisher()
}

通过这种方式,您可以轻松地处理API请求并解码响应数据。


使用Combine处理嵌套API调用

在某些情况下,您可能需要处理嵌套的API请求。以下是示例代码:

import Combine
import Foundation

func fetchTodoWithToken(token: String) -> AnyPublisher {
    let url = URL(string: "https://jsonplaceholder.typicode.com/todos/1")!
    return URLSession.shared.dataTaskPublisher(for: url)
        .tryMap { output in
            guard let response = output.response as? HTTPURLResponse, response.statusCode == 200 else {
                throw URLError(.badServerResponse)
            }
            return output.data
        }
        .decode(type: Todo.self, decoder: JSONDecoder())
        .eraseToAnyPublisher()
}

通过Combine的管道式处理,可以轻松管理多个API调用之间的依赖关系。


清理Docker环境

完成开发后,请记得停止Docker容器并清理环境。以下是相关命令:

docker ps -a
docker stop 
docker rm 
docker rmi demo-api:latest

总结

通过Combine框架,您可以显著提升Swift API客户端的可读性和可维护性。无论是处理简单的API请求,还是管理复杂的嵌套调用,Combine都提供了强大的工具支持。希望本文能帮助您更好地理解和应用Combine框架。

原文链接: https://www.andrea-scuderi.com/blog/upgrade-your-swift-api-client-with-combine