在Combine中处理错误的最佳方法是什么?

时间:2019-10-05 19:07:03

标签: swift swiftui combine

我正在尝试使用以下代码将下载的JSON解码为一个结构。

static func request(url: URL) -> AnyPublisher<SomeDecodableStruct, Error> {
    return URLSession.shared.dataTaskPublisher(for: url)
        .map { $0.data }
        .decode(type: SomeDecodableStruct.self, decoder: JSONDecoder())
        .eraseToAnyPublisher()
}

但是,如果处理失败,我希望您返回有关请求处理失败还是解码处理失败的信息。 因此,我定义了符合FailureReason协议的Error枚举,如下所示。

enum FailureReason : Error {
    case sessionFailed(error: URLError)
    case decodingFailed
}

static func request(url: URL) -> AnyPublisher<SomeDecodableStruct, FailureReason> {
    // ???
}

如何定义满足此request(url:)的{​​{1}}?

2 个答案:

答案 0 :(得分:1)

结合使用强类型的错误,因此必须使用mapError将错误转换为正确的类型,或者像RxSwift那样草率地将所有内容衰减到Error

enum NetworkService {
  enum FailureReason : Error {
      case sessionFailed(error: URLError)
      case decodingFailed
      case other(Error)
  }

  static func request<SomeDecodable: Decodable>(url: URL) -> AnyPublisher<SomeDecodable, FailureReason> {
    return URLSession.shared.dataTaskPublisher(for: url)
      .map(\.data)
      .decode(type: SomeDecodable.self, decoder: JSONDecoder())
      .mapError({ error in
        switch error {
        case is Swift.DecodingError:
          return .decodingFailed
        case let urlError as URLError:
          return .sessionFailed(error: urlError)
        default:
          return .other(error)
        }
      })
      .eraseToAnyPublisher()
  }
}

答案 1 :(得分:0)

在这种情况下,我不会用Failure以外的其他Never类型声明发布者。否则,发布者将发送其遇到的第一个错误的完成内容,并完全停止发布。最好将Output的类型设为Result。在可能产生错误的每个步骤之后,您都可以使用.mapError将其映射到您的错误类型,最后捕获错误并返回Result.failure

func request(url: URL) -> AnyPublisher<Result<SomeDecodableStruct, FailureReason>, Never> {
        return URLSession.shared.dataTaskPublisher(for: url)
                    .mapError { Error.sessionFailed(error: $0) }
                    .map { $0.data }
                    .decode(type: SomeDecodableStruct.self, decoder: JSONDecoder())
                    .map { Result<SomeDecodableStruct, FailureReason>.success($0)}
                    .mapError { _ in Error.decodingFailed }
                    .catch { Just<Result<SomeDecodableStruct, FailureReason>>(.failure($0)) }
                    .eraseToAnyPublisher()
    }