Swift HTTP Task Publisher
Swift HTTP Task Publisher 是对 DataTaskPublisher 的一种强大扩展,旨在简化并增强 Swift 项目的 HTTP 请求处理。
示例项目
要运行示例项目,请按照以下步骤操作
- 克隆仓库。
- 导航到 "Example" 目录。
- 运行
pod install
。 - 打开 Xcode 工作空间并运行示例应用。
要求
- Swift 5.5 或更高版本
- iOS 13.0 或更高版本
- macOS 10.15 或更高版本
- TVOS 13.0 或更高版本
- watchOS 8.0 或更高版本
- Xcode 13 或更高版本
安装
使用 CocoaPods 安装
要使用 CocoaPods 安装 Swift HTTP Task Publisher,请将以下行添加到您的 Podfile 中
pod 'HTTPTaskPublisher', '~> 2.0'
使用 Swift Package Manager (Xcode) 安装
- 前往 Xcode 菜单 文件 > Swift 包 > 添加包依赖
- 将 https://github.com/hainayanda/HTTPTaskPublisher.git 作为 Swift 包 URL 添加
- 将规则设置为 版本,选择 最高到下一个主要版本 选项,并使用 2.0.0 作为版本。
- 点击“下一步”,等待获取包。
从 Package.swift 使用 Swift Package Manager
要将 Swift HTTP Task Publisher
作为依赖项添加到 Swift Package Manager 项目中,请在 Package.swift
文件的依赖项中添加它
dependencies: [
.package(url: "https://github.com/hainayanda/HTTPTaskPublisher.git", .upToNextMajor(from: "2.0.0"))
]
然后,在目标的依赖项中添加 HTTPTaskPublisher
.target(
name: "YourTargetName",
dependencies: [
.product(name: "HTTPTaskPublisher", package: "HTTPTaskPublisher"),
]
)
作者
HTTPTaskPublisher 由Nayanda Haberty开发。您可以联系作者:[email protected]
许可证
HTTPTaskPublisher 在MIT许可证下发布。更详细的信息,请参阅LICENSE文件。
基本用法
执行HTTP请求时,与使用DataTaskPublisher
类似
var myRequest: URLRequest(url: url)
// ...
let cancellable = URLSession.shared.httpTaskPublisher(for: myRequest)
.sink { completion in
// do something after complete
} receiveValue: { response in
// do something with the response
}
响应类似于以下这样的元组
(data: Data, response: HTTPURLResponse)
并将发出一个类型为HTTPURLError
的错误
public indirect enum HTTPURLError: Error {
case failWhileRetry(error: Error, orignalError: HTTPURLError)
case failToRetry(reason: String, orignalError: HTTPURLError)
case failWhileAdapt(request: URLRequest, originalError: Error)
case failDecode(data: Data, response: HTTPURLResponse, decodeError: Error)
case failValidation(reason: String, data: Data, response: HTTPURLResponse)
case expectHTTPResponse(data: Data, response: URLResponse)
case urlError(URLError)
case error(Error)
}
您可以按如下方式将数据解码为所需类型:
URLSession.shared.httpTaskPublisher(for: myRequest)
.decode(type: MyObject.self, decoder: JSONDecoder())
.sink { ... }
然后它将生成一个类似以下的元组
(decoded: MyObject, response: HTTPURLResponse)
重复处理
Swift HTTP任务发布者将存储任何正在进行中的请求在URLSession
中,直到其完成。当相同的请求仍在进行中时,您可以通过在创建新的HTTPDataTaskPublisher
时传递DuplicationHandling
枚举来控制您想要如何执行请求。
URLSession.shared.httpTaskPublisher(for: myRequest, whenDuplicated: .useCurrentIfPossible)
.sink { ... }
DuplicationHandling
是一个枚举,其声明如下:
public enum DuplicationHandling {
case alwaysCreateNew
case useCurrentIfPossible
case dropIfDuplicated
}
默认选项为alwaysCreateNew
。
验证
您可以对请求添加验证,以便在响应验证失败时抛出错误
URLSession.shared.httpTaskPublisher(for: myRequest)
.validate { data, httpUrlResponse in
// validating
return .invalid(reason: "no reason")
}
.sink { ... }
闭包将在请求成功并生成 Data
和 HTTPURLResponse
后被调用。您可以对结果进行验证并返回 HTTPDataTaskValidation
作为验证结果。然后,将使用该结果来确定请求是否应传递给订阅者或抛出 HTTPURLError.failValidation(reason:data:response:)
。
HTTPDataTaskValidation
是一个像这样声明的枚举:
public enum HTTPDataTaskValidation: Equatable {
case valid
case invalid(reason: String)
}
如果要仅验证状态码,可以这样操作
URLSession.shared.httpTaskPublisher(for: myRequest)
.allowed(statusCode: 200)
.sink { ... }
// or like this
URLSession.shared.httpTaskPublisher(for: myRequest)
.allowed(statusCodes: 200..<300)
.sink { ... }
// or like this
URLSession.shared.httpTaskPublisher(for: myRequest)
.allowed(statusCodes: 200, 201)
.sink { ... }
// or using array like this
URLSession.shared.httpTaskPublisher(for: myRequest)
.allowed(statusCodes: [200, 201])
.sink { ... }
如果您需要进行更复杂的验证,只需实现 HTTPDataTaskValidator
struct MyValidator: HTTPDataTaskValidator {
func httpDataTaskIsValid(for data: Data, response: HTTPURLResponse) -> HTTPDataTaskValidation {
// validating
return .invalid(reason: "no reason")
}
}
并将其传递
URLSession.shared.httpTaskPublisher(for: myRequest)
.validate(using: MyValidator())
.sink { ... }
重试
要控制何时进行重试,可以这样做
URLSession.shared.httpTaskPublisher(for: myRequest)
.retryDecision { error, request in
// deciding
return .drop
}
.sink { ... }
当请求失败并产生 HTTPURLError
时,将调用闭包。您可以根据此结果并返回 HTTPDataTaskRetryDecision
作为重试决策结果。然后,将使用该结果来确定请求是否应重试。
HTTPDataTaskRetryDecision
是一个像这样声明的枚举:
public enum HTTPDataTaskRetryDecision: Equatable {
case retry
case dropWithReason(reason: String)
case drop
}
如果要进行更复杂的重试,可以实现 HTTPDataTaskRetrier
struct MyRetrier: HTTPDataTaskRetrier {
func httpDataTaskShouldRetry(for error: HTTPURLError, request: URLRequest) async throws -> HTTPDataTaskRetryDecision {
guard let code = error.statusCode, code == 401 else {
return .drop
}
let token = try await refreshToken()
// create a new request with token
// ...
// ...
return .retryWithNewRequest(newRequest)
}
}
并将其传递
URLSession.shared.httpTaskPublisher(for: myRequest)
.retrier(using: MyRetrier())
.sink { ... }
适应
您可以在发送请求之前这样修改您的请求
URLSession.shared.httpTaskPublisher(for: myRequest, adapter: myAdapter)
.sink { ... }
使用 HTTPDataTaskAdapter
,它是这样实现的
struct MyAdapter: HTTPDataTaskAdapter {
func httpDataTaskAdapt(for request: URLRequest) async throws -> URLRequest {
let myToken = getTokenFromCache() ?? try await refreshToken()
// create a new request with token
// ...
// ...
return newRequest
}
}
组合
由于HTTPTaskPublisher是使用Combine框架创建的,因此您可以像这样执行任何Combine允许的操作
import Combine
let payloads = try await URLSession.shared.httpTaskPublisher(for: myRequest)
.retry(2)
.map { $0.decoded.payloads }
.sink { ... }
贡献
您知道怎么做,只需克隆并提交一个pull request即可