具有泛型类型的可观察扩展
Observable extension with generic type
上下文
我想将 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() }
}
}
}
现在我想扩展 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
}
})
}
}
我尝试了多种变体,但我得到的错误是:
- 找不到'Result in scope'
- 对通用类型的引用...需要参数
有没有可能达到那样的效果?
您只需将 where 子句从 class 作用域向下移动到函数作用域(如下所示)。
也就是说,我不认为在流中间像这样脱离 monad 是“更好的通知方式”。
更好的方法是将您的 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)
现在您没有任何空订阅。
我发现了一些有用的东西:
extension ObservableType {
func progress<O: Codable>(progressCompletion: @escaping (Double) -> Void) -> Observable<UploadElement<O>> where Element == UploadElement<O> {
return self.do(onNext: { element in
if case .progress(let progress) = element {
progressCompletion(progress)
}
})
}
func response<O: Codable>(responseCompletion: @escaping (O) -> Void) -> Observable<UploadElement<O>> where Element == UploadElement<O> {
return self.do(onNext: { element in
if case .response(let response) = element {
responseCompletion(response)
}
})
}
}
现在我可以使用“计划”api:
service.update(data: /* ... */)
.progress { progress in /* */ }
.response { result in /* */ }
.subscribe(
onError: { error in /* */ }
)
.dispose(by: disposeBag)
然而,作为 Daniel ,这可能不是“更好的通知方式”。
上下文
我想将 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() }
}
}
}
现在我想扩展 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
}
})
}
}
我尝试了多种变体,但我得到的错误是:
- 找不到'Result in scope'
- 对通用类型的引用...需要参数
有没有可能达到那样的效果?
您只需将 where 子句从 class 作用域向下移动到函数作用域(如下所示)。
也就是说,我不认为在流中间像这样脱离 monad 是“更好的通知方式”。
更好的方法是将您的 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)
现在您没有任何空订阅。
我发现了一些有用的东西:
extension ObservableType {
func progress<O: Codable>(progressCompletion: @escaping (Double) -> Void) -> Observable<UploadElement<O>> where Element == UploadElement<O> {
return self.do(onNext: { element in
if case .progress(let progress) = element {
progressCompletion(progress)
}
})
}
func response<O: Codable>(responseCompletion: @escaping (O) -> Void) -> Observable<UploadElement<O>> where Element == UploadElement<O> {
return self.do(onNext: { element in
if case .response(let response) = element {
responseCompletion(response)
}
})
}
}
现在我可以使用“计划”api:
service.update(data: /* ... */)
.progress { progress in /* */ }
.response { result in /* */ }
.subscribe(
onError: { error in /* */ }
)
.dispose(by: disposeBag)
然而,作为 Daniel