如何在合并中平面映射具有不同故障类型的两个发布者

mik*_*ike 5 frp ios swift combine

我在我的 Rx 代码中遵循一个模式,我通常有一个 Observable 触发器,我 flatMap 为网络请求创建另一个 Observable。一个简化的例子:

enum ViewModelError: Error {
  case bang
}

enum DataTaskError: Error {
  case bang
}

func viewModel(trigger: Observable<Void>,
               dataTask: Observable<Result<SomeType, DataTaskError>>) -> Observable<Result<AnotherType, ViewModelError>> {
  let apiResponse = trigger
    .flatMap { dataTask }
}
Run Code Online (Sandbox Code Playgroud)

我遇到了一些麻烦的Combine等价物。我可以使用 Result 作为输出类型,使用 Never 作为失败类型,但这感觉像是对 API 的滥用。

func viewModel(trigger: AnyPublisher<Void, Never>,
               dataTask: AnyPublisher<SomeType, DataTaskError>) -> AnyPublisher<AnotherType, ViewModelError> {
  let apiResponse = trigger
    .flatMap { dataTask }
}
Run Code Online (Sandbox Code Playgroud)

我收到编译错误:

Instance method 'flatMap(maxPublishers:_:)' requires the types 'Never' and 'DataTaskError' be equivalent
Run Code Online (Sandbox Code Playgroud)

我可以使用 mapError 并将两个错误都转换为 Error,但我需要一个 DataTaskError 才能创建我的 ViewModelError。

这感觉应该不难,而且它似乎是一个相当常见的用例。我可能只是误解了一些基本原理,将不胜感激。

小智 6

当您的发布者的失败类型为 Never 时,您可以使用 setFailureType(to:)来匹配另一个发布者的失败类型。请注意,根据文档,此方法只能在故障类型为 Never 时使用。当您有实际的故障类型时,您可以将错误转换为mapError(_:). 所以你可以做这样的事情:

func viewModel(trigger: AnyPublisher<Void, Never>,
               dataTask: AnyPublisher<SomeType, DataTaskError>) -> AnyPublisher<AnotherType, ViewModelError> {
  trigger
    .setFailureType(to: ViewModelError.self) // Publisher<Void, ViewModelError>
    .flatMap {
        dataTask // Publisher<SomeType, DataTaskError>
            .mapError { _ in ViewModelError.bang } // Publisher<SomeType, ViewModelError>
            .map { _ in AnotherType() } // Publisher<AnotherType, ViewModelError>
    }
.eraseToAnyPublisher()
}
Run Code Online (Sandbox Code Playgroud)