首页 > 解决方案 > 具有泛型类型的可观察扩展

问题描述

语境

我想将其包装Alamofire.upload成一个可观察的并具有有关上传进度的信息。
为此,我创建了一个自定义UploadElement,它是一个表示进度和值或结果的枚举。到目前为止,我有:

enum UploadElement<Result> where Result: Codable {
    case progress(Double)
    case response(Result)
}

private func buildUploadRequest(url: URL, parts: [Data]) -> Observable<UploadRequest> {
    let uploadRequest = manager.upload(
        multipartFormData: { multipartFormData in /* build multipart */ },
        to: url
    )
    return Observable.just(uploadRequest)
}

func upload<Result: Codable>(url: URL, parts: [Data]) -> Observable<UploadElement<Result>> {
    buildUploadRequest(url: url, parts: parts)
        .flatMap { request in
            Observable<UploadElement<Result>>.create { observer in
                request.response { response in
                            do {
                                observer.on(.next(.response(/* decode here */)))
                                observer.on(.completed)
                            } catch let error {
                                observer.on(.error(error))
                            }
                }.uploadProgress { progress in
                    observer.on(.next(.progress(progress.fractionCompleted)))
                }
                .resume()

              return Disposable.create { request.cancel() }
            }
        }
}

现在我想对 an 进行扩展,Observable<UploadEment<Result>>以便有更好的通知方式。

基本上它会是:

service.upload(url: ..., parts: ...)
    .progress { progress in /* */ }
    .result { result in /* */ }
    .subscribe()
    .dispose(by: disposeBag)

为此,我尝试了:

extension ObservableType where Element == UploadElement<Resource> {
    func progress(progressCompletion: @escaping (Double) -> Void) -> Self {
        return self.do(onNext: { element in 
            switch element {
            case .progress(let progress): progressCompletion(progress)
            case .response: return
            }
        })
    }

    func result(resultCompletion: @escaping (Result) -> Void) -> Self {
        return self.do(onNext: { element in 
            switch element {
            case .response(let result): resultCompletion(result)
            case .progress: return
            }
        })
    }
}

我尝试了多种变体,但我得到的错误是:

  1. 找不到“范围内的结果”
  2. 引用泛型类型...必需参数

有可能实现这样的目标吗?

标签: swiftrx-swift

解决方案


您只需将 where 子句从类范围移动到函数范围(如下所示)。

也就是说,我不认为像这样在流中间突破单子是“一种更好的通知方式”。

最好将您的 Observable 分成两个流并订阅每个流:

extension ObservableType {
    func progress<Resource>() -> Observable<Double> where Element == UploadElement<Resource> {
        self.compactMap { element in
            switch element {
            case let .progress(progress):
                return progress
            case .response:
                return nil
            }
        }
    }

    func result<Resource>() -> Observable<Resource>  where Element == UploadElement<Resource> {
        self.compactMap { element in
            switch element {
            case .progress:
                return nil
            case let .response(resource):
                return resource
            }
        }
    }
}

有了上述内容,您现在可以执行以下操作:

let response = service.upload(url: ..., parts: ...)
    .share()

response
    .progress()
    .subscribe(onNext: { progress in /*...*/ })
    .disposed(by: disposeBag)

response
    .result()
    .subscribe(onNext: { result in /*...*/ })
    .dispose(by: disposeBag)

现在您没有任何空订阅。


推荐阅读