combine - 如何成功匹配两个发布者之间的失败映射(Never 和 URLError)
问题描述
在搜索了一些关于 Combine 的不同资源(包括 Joseph Heck 和 Donny Wals 的书)之后,我接近理解 DataTaskPublishers 的链接,但未能将它们连接到一系列链接运算符中。我似乎对第一个发布者的输出之间的错误与第二个发布者的预期输入不匹配这一事实感到困惑。两个 Publisher 扩展在未连接时都可以工作,所以我确信它缺乏加入两者的能力。我原以为 mapError() 会起作用,但它不想编译。
这是设置:
给定两个自定义发布者:
extension Publisher where Output == MKCoordinateRegion, Failure == URLError {
func toRegionDataTask() -> AnyPublisher<URLSession.DataTaskPublisher.Output, URLError> {
return self
.flatMap({ region -> URLSession.DataTaskPublisher in
...
...
...
return URLSession.shared.dataTaskPublisher(for: request)
})
.eraseToAnyPublisher()
}
}
和
extension Publisher where Output == [String], Failure == Never {
func toGeographiesDataTask() -> AnyPublisher<URLSession.DataTaskPublisher.Output, URLError {
return self
.setFailureType(to: URLError.self)
.flatMap({ ids -> URLSession.DataTaskPublisher in
...
...
...
return URLSession.shared.dataTaskPublisher(for: request)
})
.eraseToAnyPublisher()
}
}
然后我有一个函数试图将两者链接在一起,如下所示:
let passthroughSubj = PassthroughSubject<MKCoordinateRegion,URLError>()
passthroughSubj
.toRegionDataTask() // returns <DataTaskPublisher, URLError>
.map { $0.data } // returns <FlatMap, ?>
.decode(type: ApiResponse.self, decoder:JSONDecoder()) // returns <ApiResonse, ?>
.map {$0.body.data(using: .utf8)! } // returns <Data, ?>
.decode(type: AmznResponse.self, decoder: JSONDecoder()) // returns <AmznResponse, ?>
.map ({ response -> [AmznItem] in //
return response.contents // returns <[AmznItem], ?>
})
.map ({ items -> [String] in // returns <[String], Never> ?
var ids = [String]()
for item in items {
ids.append(item.geoid)
}
return ids
})
//
// .toGeographiesDataTask() // get error "Referencing instance method
// .map { $0.data } // 'toGeographiesDataTask()' on 'Publisher'
// .decode(type: ApiResponse.self, decoder:JSONDecoder()) // requires the types 'Error' and 'Never'
// .map {$0.body.data(using: .utf8)! } // be equivalent"
// .decode(type: AmznResponse.self, decoder: JSONDecoder())
// .map { $0.contents }
//
.sink(receiveCompletion: { (completion) in
switch completion {
case .failure(let error):
print(error)
case .finished:
print("DONE")
}
}, receiveValue: { data in
print(data)
})
.store(in: &cancellables)
passthroughSubj.send(region1)
如果我取消注释第二个自定义发布者,我会收到右侧显示的错误消息。我的理解是 .map 正在返回 <[String],Never> 但最终因为 DataTaskPublisher 可能失败,我需要将其映射到 URLError 。但是 .mapError 的组合似乎也无法编译。
我在这里错过了一些基本的东西吗?似乎是一个容易解决的问题,但我没有发现任何突出的问题。
我已经看到了使用 .flatMap 将它们链接在一起的示例,但是由于我将一个的输出转换为第二个自定义发布者的输入,这似乎是不可能的。
非常欢迎任何帮助或指示!谢谢。
解决方案
map
运算符只转换,Output
它Error
保持原样。因此,如果我要填写你Output
和Failure
对的空白,我最终会得到这个:
// returns <DataTaskPublisher, URLError>
// returns <Data, URLError>
// returns <ApiResonse, Error> (decode replaces the Failure with Error)
// returns <Data, Error>
// returns <AmznResponse, Error>
// returns <[AmznItem], Error>
// returns <[String], Error>
您的实现toGeographiesDataTask
要求它所应用的发布者具有Never
错误,这就是您得到编译器错误的原因。
我认为您可以从扩展中删除错误要求并使其成为
extension Publisher where Output == [String] {
// implementation
}
然后toGeographiesDataTask()
你可以使用以下方法替换URLError
数据任务发出的内容mapError
:
func toGeographiesDataTask() -> AnyPublisher<URLSession.DataTaskPublisher.Output, Error> {
return self
.flatMap({ ids -> AnyPublisher<URLSession.DataTaskPublisher.Output, Error> in
...
...
...
return URLSession.shared.dataTaskPublisher(for: request)
.mapError({ $0 as Error})
.eraseToAnyPublisher()
})
.eraseToAnyPublisher()
}
我认为这应该让链条的其余部分也能工作,你应该最终<[AmznItem], Error>
成为<Output, Failure>
链条的末端。
虽然我还没有在操场上尝试过,但我很确定这会让你继续前进。
推荐阅读
- autodesk-forge - 为点云添加伪造查看器测量工具
- javascript - 如何使用 javascript 获取动画网格
- html - Bootstrap Video Carousel 播放按钮/左右指示器未出现
- php - Yii2 不是从项目文件夹调用控制台命令
- metal - 在金属中画圆/半圆
- ios - Scrollview 在其中添加新组件时不会调整大小
- c# - 捕获控制选项卡生成的事件
- sql - postgresql - 累积。按月汇总活跃客户(去除流失)
- vbscript - 在 VBScript 和 XMLHTTP 中使用特殊字符
- pyspark - PySpark - 迭代数据框的行