Swift 联合收割机中处理错误的最佳方法是什么?

Swift 联合收割机中处理错误的最佳方法是什么?,swift,swiftui,combine,Swift,Swiftui,Combine,我试图用以下代码将下载的JSON解码成一个结构 static func请求(url:url)->AnyPublisher{ 返回URLSession.shared.dataTaskPublisher(用于:url) .map{$0.data} .decode(类型:SomeDecodableStruct.self,解码器:JSONDecoder()) .删除任何发布者() } 但是,如果处理失败,我希望您返回有关请求处理失败还是解码处理失败的信息。 因此,我定义了符合Error协议的Failu

我试图用以下代码将下载的JSON解码成一个结构

static func请求(url:url)->AnyPublisher{
返回URLSession.shared.dataTaskPublisher(用于:url)
.map{$0.data}
.decode(类型:SomeDecodableStruct.self,解码器:JSONDecoder())
.删除任何发布者()
}
但是,如果处理失败,我希望您返回有关请求处理失败还是解码处理失败的信息。 因此,我定义了符合
Error
协议的
FailureReason
enum,如下所示

枚举失败原因:错误{ 案例会话失败(错误:URLRERROR) 案例解码失败 } 静态func请求(url:url)->AnyPublisher{ // ??? }
我如何定义满足此
故障原因的
请求(url:)

在这种情况下,我不会声明发布者具有其他
故障类型,而不是
从不
。否则,发布者将发送一个包含其遇到的第一个错误的完成,并完全停止发布。最好是将
类型的
输出为
结果
。在每个可能产生错误的步骤之后,使用
.maperro
将其映射到错误类型,最后捕获错误并返回
结果。失败

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()
    }
func请求(url:url)->AnyPublisher{
返回URLSession.shared.dataTaskPublisher(用于:url)
.mapError{Error.sessionFailed(错误:$0)}
.map{$0.data}
.decode(类型:SomeDecodableStruct.self,解码器:JSONDecoder())
.map{Result.success($0)}
.mapError{错误中的{.decodingFailed}
.catch{Just(.failure($0))}
.删除任何发布者()
}

Combine是针对错误的强类型,因此您必须使用
mapError
将错误转换为正确的类型,或者像RxSwift一样草率,将所有内容衰减为
错误

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()
  }
}
枚举网络服务{ 枚举失败原因:错误{ 案例会话失败(错误:URLRERROR) 案例解码失败 案例其他(错误) } 静态func请求(url:url)->AnyPublisher{ 返回URLSession.shared.dataTaskPublisher(用于:url) .map(\.data) .decode(类型:SomeDecodable.self,解码器:JSONDecoder()) .mapError({中的错误 开关错误{ 案例为Swift。解码错误: 返回。解码失败 case let urleror作为urleror: return.sessionFailed(错误:urlError) 违约: return.other(错误) } }) .删除任何发布者() } }
谢谢您的回答。使用
Result
的想法很棒。我有个问题。源代码是否通过第二个
mapError
.sessionFailure
转换为
decodingFailure
?。mapError将此时可能引发的错误(在本例中)更改为任何其他类型的错误。由于您的FailureReason已将sessionFailure声明为具有关联类型的URLError,因此您需要在初始化此案例时将其传入。除非您正在回收此发布服务器(即flatMap内部),否则我不会将错误具体化为结果。即使这样,通常也有一种比使用物化或手动物化更好的模式来捕获错误。@JoshHomann用这种方式手动物化错误,我不会遇到任何问题。很好的解决方案!只有一个问题,我看到的第一件事是映射数据
.map(\.data)
,因此
dataTaskPublisher
输出的响应被忽略,那么如果某个API返回HTTP 404之类的错误,我们如何捕获它呢?@ChuckZHB使用catch操作符和Fail publisher:
。catch{Fail(…)}