How to Decode the Body of an Error in Alamofire 5

How to decode the body of an error in Alamofire 5?

I ended up making it work with the following ResponseSerializer:

struct APIError: Error, Decodable {
let message: String
let code: String
let args: [String]
}

final class TwoDecodableResponseSerializer<T: Decodable>: ResponseSerializer {

lazy var decoder: JSONDecoder = {
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
return decoder
}()

private lazy var successSerializer = DecodableResponseSerializer<T>(decoder: decoder)
private lazy var errorSerializer = DecodableResponseSerializer<APIError>(decoder: decoder)

public func serialize(request: URLRequest?, response: HTTPURLResponse?, data: Data?, error: Error?) throws -> Result<T, APIError> {

guard error == nil else { return .failure(APIError(message: "Unknown error", code: "unknown", args: [])) }

guard let response = response else { return .failure(APIError(message: "Empty response", code: "empty_response", args: [])) }

do {
if response.statusCode < 200 || response.statusCode >= 300 {
let result = try errorSerializer.serialize(request: request, response: response, data: data, error: nil)
return .failure(result)
} else {
let result = try successSerializer.serialize(request: request, response: response, data: data, error: nil)
return .success(result)
}
} catch(let err) {
return .failure(APIError(message: "Could not serialize body", code: "unserializable_body", args: [String(data: data!, encoding: .utf8)!, err.localizedDescription]))
}

}

}

extension DataRequest {
@discardableResult func responseTwoDecodable<T: Decodable>(queue: DispatchQueue = DispatchQueue.global(qos: .userInitiated), of t: T.Type, completionHandler: @escaping (Result<T, APIError>) -> Void) -> Self {
return response(queue: .main, responseSerializer: TwoDecodableResponseSerializer<T>()) { response in
switch response.result {
case .success(let result):
completionHandler(result)
case .failure(let error):
completionHandler(.failure(APIError(message: "Other error", code: "other", args: [error.localizedDescription])))
}
}
}
}

And with that, I can call my API like so:

AF.request(request).validate().responseTwoDecodable(of: [Item].self) { response in
switch response {
case .success(let items):
completion(.success(items))
case .failure(let error): //error is an APIError
log.error("Error while loading items: \(String(describing: error))")
completion(.failure(.couldNotLoad(underlyingError: error)))
}
}

I simply consider that any status code outside of the 200-299 range corresponds to an error.

Decoding an Error Response using Alamofire 5 and the responseDecodable function

Similiar login function from the article you mentioned, updated for current beta of Alamofire 5 and dealing with 404, 401 etc. (via the guard statement)

  static func login(email: String, password: String, completion:@escaping (Result<UserCredentials>)->Void) {
performRequest(router: Router.login(email: email, password: password), completion: completion)
AF.request(Router.login(email: email, password: password))
.validate(statusCode: 200..<300)
.responseDecodable { (response: DataResponse<UserCredentials>) in
guard response.result.isSuccess else {
print(" Error on login: \(String(describing: response.error))")
return
}
completion(response.result)
}
}

Any way to get the response body during HTTP errors?

I asked this question on their github page and got an answer from cnoon:

swift 2:

if let data = response.data {
let json = String(data: data, encoding: NSUTF8StringEncoding)
print("Failure Response: \(json)")
}

swift 3:

if let data = response.data {
let json = String(data: data, encoding: String.Encoding.utf8)
print("Failure Response: \(json)")
}

https://github.com/Alamofire/Alamofire/issues/1059

I just left out the encoding part, by doing this you are able to get the response json even in the case of errors.

How to print result from alamofire response using codable

You can print the raw Data in your responseDecodable closure by grabbing it from the DataResponse:

print(response.data.map { String(decoding: $0, as: UTF8.self) } ?? "No data.")

You can also add a separate serializer just to see the String:

.responseDecodable { }
.responseString { }

If you just want to see the response for debugging, you can debugPrint the response in the closure. This will print the request and response body datas as Strings.

.responseDecodable(of: T.self) { response in
debugPrint(response)
}

How do I get Data value from Combine + Alamofire Error response?

Can you please check if this solves your problem?

AccountAPI.postAccount(withSignUpCommand: signUpCommand)
.sink(receiveCompletion: { [weak self] result in
guard let self = self else { return }

switch result {
case .finished:
self.logger.debug("signUp completed.")
case .failure(let error):
self.isAuthenticating = false
self.logger.error("signUp error : \(error)")

if case let ErrorResponse.error(statusCode, data, error) = error {

// TODO: Decode `data` here...

}
}
}, receiveValue: { [weak self] (statusCode, userId) in
guard let self = self else { return }

self.logger.debug("signUp status code : \(statusCode)")
})
.store(in: &disposables)

How to handle empty response using Alamofire and Combine?

This error occurs when your backend returns no data but does not return an appropriate HTTP response code (204 or 205). If this is expected behavior for your backend, you can add your response code to the list of acceptable empty response codes when you set up the publisher: .publishDecodable(T.self, emptyResponseCodes: [200]. This also requires T to either conform to Alamofire's EmptyResponse protocol, or for you to expect Alamofire's Empty type as the response.



Related Topics



Leave a reply



Submit