ComposableRequest 是一个基于声明式接口的网络层,使用(现代)Swift 编写。
它抽象化了 URLSession
的实现,为了提供简洁而强大的端点表示(包括请求和响应),开箱即用地支持 Combine Publisher
和结构化并发 (async
/await
),只需一次定义。
它自带 Storage
(在 Storage 模块中),这是一种缓存 Storable
项的方式,并附带相关的具体实现(例如 UserDefaultsStorage
、KeychainStorage
– 您需要添加 StorageCrypto 才能使用 KeychainStorage
,这取决于 KeychainAccess),以及允许 API 封装器的最终用户通过 Provider
注入代码的能力。
您可以在每个发布版本下直接找到所有变更日志。
下一步是什么?
ComposableRequest 最初是 Swiftagram 的网络层,并且它仍然倾向于遵循大致相同的开发周期。
欢迎通过发送 pull request 来贡献代码。请务必事先参考我们的贡献指南和行为准则。
File
/Swift Packages
/Add Package Dependency…
。https://github.com/sbertix/ComposableRequest.git
。为什么不使用 CocoaPods、Carthage 或
blank?
支持多种依赖管理器会使维护库的复杂性和时间成本呈指数级增长。
此外,随着 Swift Package Manager 集成到 Xcode 11 及更高版本中,我们预计对替代解决方案的需求将迅速消退。
查看 Swiftagram 或访问(自动生成的)文档,了解 Requests、Storage 和 StorageCrypto 的用例。
作为一个实现示例,我们可以展示一些与 Instagram 端点相关的代码,该端点负责删除帖子。
public extension Request {
/// An enum listing an error.
enum DeleteError: Swift.Error { case invalid }
/// Delete one of your own posts, matching `identifier`.
/// Checkout https://github.com/sbertix/Swiftagram for more info.
///
/// - parameter identifier: A valid `String`.
/// - returns: A locked `AnySingleEndpoint`, waiting for authentication `HTTPCookie`s.
func delete(_ identifier: String) -> Providers.Lock<[HTTPCookie], AnySingleEndpoint<Bool>> {
// Wait for user defined values.
.init { cookies in
// Fetch first info about the post to learn if it's a video or picture
// as they have slightly different endpoints for deletion.
Single {
Path("https://i.instagram.com/api/v1/media/\(identifier)/info")
// Wait for the user to `inject` an array of `HTTPCookie`s.
// You should implement your own `model` to abstract away
// authentication cookies, but as this is just an example
// we leave it to you.
Headers(HTTPCookie.requestHeaderFields(with: cookies))
// Decode it inside an `AnyDecodable`, allowing to interrogate JSON
// representations of object without knowing them in advance.
Response {
let output = try JSONDecoder().decode(AnyDecodable.self, from: $0)
guard let type = output.items[0].mediaType.int,
[1,2, 8].contains(type) else {
throw DeleteError.invalid
}
return type
}
}.switch {
Path("https://i.instagram.com/api/v1/media/\(identifier)/delete")
Query($0 == 2 ? "VIDEO" : "PHOTO", forKey: "media_type")
// This will be applied exactly as before, but you can add whaterver
// you need to it, as it will only affect this `Request`.
Headers(HTTPCookie.requestHeaderFields(with: cookies))
Response {
let output = try JSONDecoder().decode(AnyDecodable.self, from: $0)
return $0.status.bool ?? false
}
}
}
}
}
那么用户如何检索信息呢?
用户只需…
/// A valid post identifier.
let identifier: String = /* a valid String */
/// A valid array of cookies.
let cookies: [HTTPCookie] = /* an array of HTTPCookies */
/// A *retained* collection of `AnyCancellable`s.
var bin: Set<AnyCancellable> = []
/// Delete it using **Combine**.
Request.delete(identifier)
.unlock(with: cookies)
.resolve(with: .shared) // The shared `URLSession`.
.sink { _ in } receiveValue: { print($0) }
.store(in: &bin)
[…]
/// Delete it using _async/await_.
let result = try await Request.delete(identifier)
.unlock(with: cookies)
.resolve(with: .shared)
如果取消请求或稍后启动请求呢?
Receivable
的具体实现可能会通过其底层类型(如 URLSessionDataTask
或 Cancellable
)来实现暂停和取消。
通过遵循 Storage
协议,特别是通过实现 ThrowingStorage
或 NonThrowingStorage
之一,可以提供 Storable
的缓存。
该库附带了几个具体的实现。
TransientStorage
,并且当没有提供 Storage
时,Authenticator
默认使用 TransientStorage
。UserDefaultsStorage
允许更快地进行开箱即用的测试,但不建议在生产环境中使用,因为私有 Cookie 未加密。KeychainStorage
(需要您添加 ComposableStorageCrypto,首选)将 Cookie 安全地存储在用户的钥匙串中。