Пользовательская обработка ответов при ошибках с помощью Moya + RxSwift

Я использую Moya с RxSwift для работы в сети в приложении iOS, и я хотел бы иметь возможность использовать пользовательские ответы об ошибках моего API, когда мои Observers получают вызовы onError.

API всегда возвращает ответы об ошибках в следующем формате JSON:

{
   "error": {
      "message": "Error message goes here",
      "code": "String error code"
   }
}

Цель состоит в том, чтобы добиться чего-то похожего на следующий фрагмент кода, где ошибка, переданная в onError, является моим настраиваемым типом ошибки, а не типом Moya.Error:

observable
    .subscribe(
        onNext: { response in
            // Do typical onNext stuff
        },
        onError: { error in
            // Get and consume my custom error type here:
            let customError = error as! MyCustomErrorModel
            print("My API's error message: \(customError.error?.message)")
            print("My API's error code: \(customError.error?.code)")
        })

Я могу успешно перехватывать и десериализовать эти ошибки в мою пользовательскую модель ошибок, используя пользовательский PluginType (вставлен ниже, из этот вопрос SO), но я не знаю, как наконец передать эти модели Observer.

import Foundation
import Moya
import ObjectMapper
import Result

struct CustomAPIErrorPlugin: PluginType {

// Called immediately before a request is sent over the network (or stubbed).
func willSendRequest(request: RequestType, target: TargetType) { }

// Called after a response has been received, but before the MoyaProvider has invoked its completion handler.
func didReceiveResponse(result: Result<Moya.Response, Moya.Error>, target: TargetType) {
    let responseJSON: AnyObject
    if let response = result.value {
        do {
            responseJSON = try response.mapJSON()
            if let errorResponse = Mapper<MyCustomErrorModel>().map(responseJSON) {
                print("Custom error code from server: \(errorResponse.error?.code)")
            }
        } catch {
            print("Failure to parse JSON response")
        }
    } else {
        print("Network Error = \(result.error)")
    }
}

person Scott Storch    schedule 27.08.2016    source источник


Ответы (1)


Я бы посоветовал расширить ObservableType, поскольку в конечном итоге это самое чистое решение, когда мы говорим об обработке ответов об ошибках api. Что-то вроде ниже (не проверено ...)

extension ObservableType where E == Response {
  func filterSuccess() -> Observable<E> {
    return flatMap { (response) -> Observable<E> in
        if 200 ... 299 ~= response.statusCode {
            return Observable.just(response)
        }

        if let errorJson = response.data.toJson(), 
           let error = Mapper<MyCustomErrorModel>().map(errorJson) {
            return Observable.error(error)
        }

        // Its an error and can't decode error details from server, push generic message
        let genericError = MyCustomErrorModel.genericError(code: response.statusCode, message: "Unknown Error")
        return Observable.error(genericError)
    }
}

Вот как вы это используете

provider.request(.test)
        .filterSuccess()
        .mapJSON()
        .subscribe { [unowned self] e in
            switch e {
            case .next(let json as JSON):
            case .error(let error as MyCustomErrorModel):
            // Handle your custom error here
            default: break
            }
    }.disposed(by: disposeBag)
person TheiOSChap    schedule 20.04.2017