junbok
junbok

Reputation: 9

how to use alamofire httpstatusCode

I want a diffrerent responseDecodable on the httpStatusCode

server return

if statusCode == 200
resonseBody
{id: number}

if statusCode 400..<500
resonseBody
{
code: String
timestamp: String
message: String
}

so now my code is

 AF.request(url, method: .post, headers: header).responseData { response in
            switch response.result {
            case .success(let data) :
                guard let response = response.response else {return}
                let json = try? JSONSerialization.jsonObject(with: data)
                switch response.statusCode {
                    case 200:
                    if let json = json as? [String: Any] , let message = json["id"] as? Int{print(message)}
                case (400..<500):
                    if let json = json as? [String: Any] , let message = json["message"] as? String{print(message)}
                default:
                    return
                }
            case .failure(let err) :
                print(err)
            }
        }

I try this code convert responseDecodable

struct a: Codable {var id: Int}

struct b: Codable{
    var code: String
    var timestamp: String
    var message: String
}

AF.request(url, method: .post, headers: header).responseDecodable(of: a.self) { response in
            guard let data = response.value else {return}
            print(data)
            }
            .responseDecodable(of: b.self) { response in
            guard let data = response.value else {return}
            print(data)
            }

but this way Regardless statusCode return both a and b

I want stautsCode == 200 return a or statusCode 400..<500 return b

What should I Do?

Upvotes: 0

Views: 936

Answers (1)

Rob
Rob

Reputation: 437412

AFAIK, Alamofire does not have a “decode one object for success and another for failure” implementation. You'll have to do this yourself.

If you really want a one distinct object for 2xx responses and another for 4xx responses, there are a few approaches:

  1. Use validate to handle the 2xx responses, and manually decode 4xx responses in the error handler.

    AF.request(url, method: .post, parameters: parameters, headers: header)
        .validate(statusCode: 200 ..< 300)             // only 2xx are auto-decoded for us; handle 4xx responses in `failure` handler
        .responseDecodable(of: Foo.self) { response in
            switch response.result {
            case .failure(let error):
                guard
                    let statusCode = response.response?.statusCode,
                    400 ..< 500 ~= statusCode,
                    let data = response.data,
                    let apiError = try? JSONDecoder().decode(ApiErrorResponse.self, from: data)
                else {
                    print("other error:", error)      // didn't parse `ApiErrorResponse` object, so obviously some other error
                    return
                }
    
                print("apiError:", apiError)          // this is our parsed API error object
    
            case .success(let foo):
                print("success:", foo)
            }
        }
    
  2. If you want, you could write your own ResponseSerializer to parse 2xx and 4xx responses differently:

    struct ApiErrorResponse: Decodable, Error {
        let code: String
        let timestamp: String
        let message: String
    }
    
    final class ApiResponseSerializer<T: Decodable>: ResponseSerializer {
        lazy var decoder = JSONDecoder()
    
        private lazy var successSerializer = DecodableResponseSerializer<T>(decoder: decoder)
        private lazy var errorSerializer = DecodableResponseSerializer<ApiErrorResponse>(decoder: decoder)
    
        public func serialize(request: URLRequest?, response: HTTPURLResponse?, data: Data?, error: Error?) throws -> T {
            if let error = error { throw error }
    
            guard let response = response else { throw URLError(.badServerResponse) }
    
            switch response.statusCode {
                case 400 ..< 500:
                let apiErrorObject = try errorSerializer.serialize(request: request, response: response, data: data, error: nil)
                throw apiErrorObject
    
            default:
                return try successSerializer.serialize(request: request, response: response, data: data, error: nil)
            }
        }
    }
    

    And then you can do:

    AF.request(url, method: .post, parameters: parameters, headers: header)
        .response(responseSerializer: ApiResponseSerializer<Foo>()) { response in
            switch response.result {
            case .failure(.responseSerializationFailed(reason: .customSerializationFailed(error: let apiErrorObject))):
                print("api response error:", apiErrorObject)
    
            case .failure(let error):
                print("some other error:", error)
    
            case .success(let foo):
                print("success:", foo)
            }
        }
    

    I find the nesting of the parsed error object to be a little tedious, but it does abstract the 2xx vs 4xx logic out of the response/responseDecoder completion handler.

In both of these, I'm considering all 2xx responses as success, not just 200. Some servers return 2xx codes other than just 200.

Upvotes: 0

Related Questions