【问题标题】:How can I decode custom Error with Combine?如何使用 Combine 解码自定义错误?
【发布时间】:2020-11-30 09:59:15
【问题描述】:

我正在调用的 API 可以返回包含错误消息的 JSON。

如果我希望在成功请求时返回另一个 Decodable 对象,我如何告诉 Combine 尝试解码此自定义错误?

我的代码目前如下所示:

    private var cancellable: AnyCancellable?

    internal func perform<T>(request: URLRequest, completion: @escaping (Result<T, Error>) -> Void) where T: Decodable {
            cancellable = session.dataTaskPublisher(for: request)
                .tryMap { output in
                    guard let response = output.response as? HTTPURLResponse, response.statusCode == 200 else {
                        throw HTTPError.statusCode
                    }
                    return output.data
                }
                .decode(type: T.self, decoder: JSONDecoder())
                .eraseToAnyPublisher()
                .sink(receiveCompletion: { _completion in
                    guard case .failure(let error) = _completion else {
                        return
                    }
                    completion(.failure(error))
                }, receiveValue: { value in
                    completion(.success(value))
                })
}

有了URLSession,我会做这样的事情:

URLSession.shared.dataTask(with: request) { data, response, error in
    // Check for any connection errors
    if let error = error {
        completion(.failure(error))
        return
    }
    // Read data
    guard let data = data, !data.isEmpty else {
        completion(.failure(SPTError.noDataReceivedError))
        return
    }
    // Check response's status code, if it's anything other than 200 (OK), try to decode SPTError from the data.
    guard let httpResponse = response as? HTTPURLResponse,
          httpResponse.statusCode == 200 else {
        let sptError = (try? JSONDecoder().decode(SPTError.self, from: data)) ?? SPTError.badRequest
        completion(.failure(sptError))
        return
    }
    // Decode requested objects
    do {
        let object = try JSONDecoder().decode(T.self, from: data)
        completion(.success(object))
    } catch {
        print(completion(.failure(error)))
    }
}.resume()

SPTError只是一个包含代码和消息的结构体,它符合Codable

【问题讨论】:

  • 你当前的代码有什么问题?
  • 它只尝试解码T 对象,如果响应与200 代码不同,它会引发一般错误,我想将数据解码为SPTError

标签: ios swift nsurlsession combine urlsession


【解决方案1】:

当您有条件分支时,您可以使用.flatMap 根据您检查的任何条件确定返回哪个发布者。

FlatMap 必须匹配上游的失败类型和返回的发布者,所以需要先将.mapError 匹配到一个通用的Error。并且因为不同的分支是不同的发布者链,所以输入erase all to AnyPublisher:

URLSession.shared.dataTaskPublisher(for: url)
   .mapError { $0 as Error }
   .flatMap() { output -> AnyPublisher<T, Error> in
      if output.data.isEmpty {
         return Fail(error: SPTError.noDataReceivedError).eraseToAnyPublisher()
      }
        
      guard let httpResponse = output.response as? HTTPURLResponse else {
         return Fail(error: HTTPError.statusCode).eraseToAnyPublisher()
      }
        
      if httpResponse.statusCode == 200 {
         return Just(output.data)
            .decode(type: T.self, decoder: JSONDecoder())
            .eraseToAnyPublisher()
      } else {
         return Just(output.data)
            .decode(type: SPTError.self, decoder: JSONDecoder())
            .flatMap { Fail(error: $0) }
            .eraseToAnyPublisher()
       }
   }
   .eraseToAnyPublisher()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-03
    • 2020-09-30
    相关资源
    最近更新 更多