Search code examples
swiftalamofire

How to parse error when api request fails using responsedecodable in swift alamofire


I am trying to do an API request to the backend using alamofire and responseDecodable.

AF.request(Router.registerFacebookUser(facebookToken: token)).validate().responseDecodable(of: UserConfig.self) { result in
        switch result.result {
        case let .success(userConfig):
            onAuthentication(userConfig)
        case let .failure(error):
            print(error)
            //somehow get the message from ERROR JSON and pass it here
            onFailure(error.localizedDescription)
        }
    }

When call succeeds, it successfully parses JSON to the model. However, there as some special cases, when it should fail. For example if user is already registered, I get a response JSON:

{
   "error":{
      "message":"User already exist"
   }
}

Is it possible to override the AF error that we receive? Or maybe it's possible to parse another object if request fails? Or are there other ways how I can access the error message?


Solution

  • There are several ways to approach this in Alamofire.

    1. In the validate() method that takes a closure, parse the error body and produce a .failure result with a custom associated error:
    .validate { request, response, data
        // Check request or response state, parse data into a custom Error type.
        return .failure(MyCustomErrorType.case(parsedError))
    }
    

    Then, in your response handler, you'll need to cast to your custom error type from the AFError's underlyingError property:

    .responseDecodable(of: SomeType.self) { response in
        switch response.result {
        case let .success(value): // Do something.
        case let .failure(error):
            let customError = error.underlyingError as? MyCustomErrorType
            // Do something with the error, like extracting the associated value.
    }
    
    1. Use a Decodable container type to parse your responses as either the type you expect or your error representation. You should be able to find examples elsewhere, but on Alamofire's side it would work like this:
    .responseDecodable(of: ContainerType<SomeType>.self) { response in
        // Do something with response.
    }
    
    1. Write a custom ResponseSerializer type that checks the response and parses the error type when a failure is detected, otherwise parsing the expected type. We have examples in our documentation.

    Of these options I usually go with the wrapper type unless I'm already using my own custom Error type, in which case the validator is fairly easy. A custom serializer is the most work but gives you the most flexibility as well, especially if you need to customize other aspects of your response handling.