繁体   English   中英

为 Alamofire 请求创建像 RxSwift 的 Observable.Create 这样的 Combine 发布者

[英]Creating a Combine's publisher like RxSwift's Observable.Create for an Alamofire request

我使用以下代码生成冷RxSwift Observable

func doRequest<T :Mappable>(request:URLRequestConvertible) -> Observable<T> {
        let observable = Observable<T>.create { [weak self] observer in
        guard let self = self else { return Disposables.create() }
        self.session.request(request).validate().responseObject { (response: AFDataResponse<T>) in
            switch response.result {
                case .success(let obj):
                    observer.onNext(obj)
                    observer.onCompleted()
                case .failure(let error):
                    let theError = error as Error
                    observer.onError(theError)
            }
        }
         return Disposables.create()
    }
    return observable
}

其中Mappable是基于ObjectMapper的类型,而self.session是 Alamofire 的Session对象。

我在 Apple 的Combine框架中找不到与Observable.create {...}等效的东西。 我只发现URLSession.shared.dataTaskPublisher(for:)它使用 Apple 的URLSession类创建发布者。

如何将上述 observable 转换为 Alamofire Combine 的发布者?

编辑:使用 rob 提供的解决方案,我得到了以下结果:

 private let apiQueue = DispatchQueue(label: "API", qos: .default, attributes: .concurrent)

  func doRequest<T>(request: URLRequestConvertible) -> AnyPublisher<T, AFError> where T : Mappable {

       Deferred { [weak self] () -> Future<T, AFError> in

          guard let self = self else {
              return Future<T, AFError> { promise in  
promise(.failure(.explicitlyCancelled))  }
        }

          return Future { promise in
            self.session
            .request(request)
            .validate()
            .responseObject { (response: AFDataResponse<T>) in
                promise(response.result)
            }
        }
    }
    .handleEvents(receiveCompletion: { completion in
        if case .failure (let error) = completion {
                //handle the error
        }
    })
    .receive(on: self.apiQueue)
    .eraseToAnyPublisher()
}

EDIT2:我必须删除私有队列,因为它不需要,Alamofire 自己解析解码,因此删除队列及其用法( .receive(on: self.apiQueue)

您可以使用FutureresponseObject的回调连接到 Combine Publisher 我没有方便的 Alamofire 进行测试,但我认为以下应该有效:

func doRequest<T: Mappable>(request: URLRequestConvertible) -> AnyPublisher<T, AFError> {
    return Future { promise in
        self.session
            .request(request)
            .validate()
            .responseObject { (response: AFDataResponse<T>) in
            promise(response.result)
        }
    }.eraseToAnyPublisher()
}

请注意,这比 RxSwift 版本要简单一些,因为promise直接接受Result ,因此我们不必切换response.result

Future是一种“不冷不热”的出版商。 它就像一个 hot observable 因为它立即执行它的 body 并且只执行一次,所以它立即启动 Alamofire 请求。 它也像一个冷的 observable,因为每个订阅者最终都会收到一个值或一个错误(假设你最终调用了promise )。 Future只执行一次它的主体,但它会缓存你传递给promiseResult

您可以通过将Future包装在Deferred来创建一个真正冷的发布者:

func doRequest<T: Mappable>(request: URLRequestConvertible) -> AnyPublisher<T, AFError> {
    return Deferred {
        Future { promise in
            self.session
                .request(request)
                .validate()
                .responseObject { (response: AFDataResponse<T>) in
                    promise(response.result) }
        }
    }.eraseToAnyPublisher()
}

每次订阅时, Deferred调用它的主体来创建一个新的内部Publisher者。 因此,每次订阅时,您都会创建一个新的Future ,它将立即启动一个新的 Alamofire 请求。 如果您想使用retry运算符,这很有用,如本问题所示

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM