Alamofire responseDecodable for model for response and error

3.4k views Asked by At

I'm using Alamofire to make API calls from mobile app. I have struct which is map from Alamofire API calls this way

APIManager.shared.session.request(UserRouter.signUp(username, password)).responseDecodable (of: User.self) { response in
  complition(response.value, response.error)
}

When API call is failed and returned API error is JSON format I'm getting AFError

▿ Optional<AFError>
  ▿ some : AFError
    ▿ responseSerializationFailed : 1 element
      ▿ reason : ResponseSerializationFailureReason
        ▿ decodingFailed : 1 element
          ▿ error : DecodingError
            ▿ keyNotFound : 2 elements
              - .0 : ContainerKeys(stringValue: "user", intValue: nil)
              ▿ .1 : Context
                - codingPath : 0 elements
                - debugDescription : "Cannot get KeyedDecodingContainer<CodingKeys> -- no value found for key ContainerKeys(stringValue: \"user\", intValue: nil) (\"user\")"
                - underlyingError : nil

This is what API returning back on that call

{
  "success": false,
  "errors": [
    "Email can't be blank",
    "Password can't be blank"
  ]
}

I end up writing this to get it handle:

struct APIError: Error, Decodable {
    var success: Bool
    var errors: [String]
}
    APIManager.shared.session.request(UserRouter.signUp(username, password)).responseDecodable (of: User.self) { response in
        switch response.result {
        case .success(let value):
            complition(value, nil)
        case .failure:
            let somethingWrong = APIError(success: false, errors: ["Something went wrong. Please try again."])

            guard let data = response.data else {
                complition(nil, somethingWrong)
                return
            }
            
            do {
                let error = try JSONDecoder().decode(APIError.self, from: data)
                complition(nil, error)
            } catch {
                complition(nil, somethingWrong)
            }

        }
    }

How this code can be written better way, maybe Alamofire does support map error model as well.

1

There are 1 answers

0
Jon Shier On

A typical solution here is to use an enum as your base response type.

enum APIResult<Success: Decodable> {
  case success(Success)
  case failure(APIError)
}

You can then either make the enum directly conform to Decodable (there are many solutions out there for that), or create your own Alamofire ResponseSerializer which handles the decoding logic for you. That approach is more work but is more flexible, as you can consider the request and response when parsing.