Header


Swift
iOS macOS tvOS watchOS


ComposableRequest 是一个基于声明式接口的网络层,使用(现代)Swift 编写。

它抽象化了 URLSession 的实现,为了提供简洁而强大的端点表示(包括请求和响应),开箱即用地支持 Combine Publisher结构化并发 (async/await),只需一次定义。

它自带 Storage (在 Storage 模块中),这是一种缓存 Storable 项的方式,并附带相关的具体实现(例如 UserDefaultsStorageKeychainStorage – 您需要添加 StorageCrypto 才能使用 KeychainStorage,这取决于 KeychainAccess),以及允许 API 封装器的最终用户通过 Provider 注入代码的能力。

状态

push GitHub release (latest by date)

您可以在每个发布版本下直接找到所有变更日志。

下一步是什么?

ComposableRequest 最初是 Swiftagram 的网络层,并且它仍然倾向于遵循大致相同的开发周期。

里程碑问题 是了解最新开发动态的最佳方式。

欢迎通过发送 pull request 来贡献代码。请务必事先参考我们的贡献指南行为准则

安装

Swift Package Manager (Xcode 11 及以上版本)

  1. 从菜单中选择 File/Swift Packages/Add Package Dependency…
  2. 粘贴 https://github.com/sbertix/ComposableRequest.git
  3. 按照步骤操作。
  4. 添加 StorageRequests 以获得完整体验。

为什么不使用 CocoaPods、Carthage 或 blank

支持多种依赖管理器会使维护库的复杂性和时间成本呈指数级增长。
此外,随着 Swift Package Manager 集成到 Xcode 11 及更高版本中,我们预计对替代解决方案的需求将迅速消退。

目标

用法

查看 Swiftagram 或访问(自动生成的)文档,了解 RequestsStorageStorageCrypto 的用例。

端点

作为一个实现示例,我们可以展示一些与 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 的具体实现可能会通过其底层类型(如 URLSessionDataTaskCancellable)来实现暂停和取消。

缓存

通过遵循 Storage 协议,特别是通过实现 ThrowingStorageNonThrowingStorage 之一,可以提供 Storable 的缓存。

该库附带了几个具体的实现。