fix(api): map API response error payload and validate card saving synchronously

This commit is contained in:
Daniel Arantes Loverde
2026-06-04 16:06:22 -03:00
parent 8c19b1323c
commit 39ddf9c86c
2 changed files with 80 additions and 21 deletions

View File

@@ -111,6 +111,11 @@ private extension ApiClient {
guard let data = responseString.data(using: .utf8) else {
throw NetworkError.decodeError("Resposta nao UTF-8")
}
if let object = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
let errorFlag = object["error"] as? Bool, errorFlag {
let message = object["message"] as? String ?? object["msg"] as? String
throw NetworkError.httpError(200, message ?? "Erro no servidor")
}
do {
return try JSONDecoder().decode(T.self, from: data)
} catch {
@@ -247,6 +252,12 @@ private extension ApiClient {
throw NetworkError.unauthorized(payload?.message)
}
if let object = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
let errorFlag = object["error"] as? Bool, errorFlag {
let message = object["message"] as? String ?? object["msg"] as? String
throw NetworkError.httpError(http.statusCode, message ?? "Erro no servidor")
}
if http.statusCode == 429 {
let retryAfter = Int(http.value(forHTTPHeaderField: "Retry-After") ?? "")
throw NetworkError.rateLimited(retryAfter)
@@ -309,6 +320,14 @@ private extension ApiClient {
if let code = payload?.code, code.isEmpty == false {
return "Erro: \(code)"
}
// fallback if error true is present but without a classic structure
if let object = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
let errorFlag = object["error"] as? Bool, errorFlag {
if let msg = object["message"] as? String ?? object["msg"] as? String {
return msg
}
}
return String(data: data, encoding: .utf8)
}
@@ -318,9 +337,10 @@ private extension ApiClient {
}
if let object = try? JSONSerialization.jsonObject(with: data) as? [String: Any] {
let errorFlag = object["error"] as? Bool ?? false
let code = object["code"] as? String
let message = object["message"] as? String
if code != nil || message != nil {
let message = object["message"] as? String ?? object["msg"] as? String ?? object["error_description"] as? String
if errorFlag || code != nil || message != nil {
return ApiErrorDescriptor(code: code, message: message)
}
}
@@ -344,14 +364,42 @@ private extension ApiClient {
#if canImport(LCEssentials) && os(iOS)
func serverMessage(from error: NSError) -> String? {
if let reason = error.userInfo[NSLocalizedFailureReasonErrorKey] as? String,
!reason.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
if let data = reason.data(using: .utf8),
let parsed = serverMessage(from: data),
!parsed.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
return parsed
// Tenta buscar no userInfo por chaves conhecidas
for key in [NSLocalizedFailureReasonErrorKey, "body", "responseBody", "data", "message"] {
if let value = error.userInfo[key] as? String,
!value.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
if let data = value.data(using: .utf8),
let parsed = serverMessage(from: data),
!parsed.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
if parsed.trimmingCharacters(in: .whitespacesAndNewlines).hasPrefix("{") {
continue
}
return parsed
}
if !value.trimmingCharacters(in: .whitespacesAndNewlines).hasPrefix("{") {
return value
}
} else if let valueData = error.userInfo[key] as? Data {
if let parsed = serverMessage(from: valueData),
!parsed.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty,
!parsed.trimmingCharacters(in: .whitespacesAndNewlines).hasPrefix("{") {
return parsed
}
}
}
// Tenta varrer qualquer chave do userInfo por strings que possam ser JSON ou mensagens
for (_, value) in error.userInfo {
if let str = value as? String, !str.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
if str.trimmingCharacters(in: .whitespacesAndNewlines).hasPrefix("{") {
if let data = str.data(using: .utf8),
let parsed = serverMessage(from: data),
!parsed.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty,
!parsed.trimmingCharacters(in: .whitespacesAndNewlines).hasPrefix("{") {
return parsed
}
}
}
return reason
}
if let description = error.userInfo[NSLocalizedDescriptionKey] as? String,
@@ -364,10 +412,24 @@ private extension ApiClient {
}
func serverPayload(from error: NSError) -> ApiErrorDescriptor? {
if let reason = error.userInfo[NSLocalizedFailureReasonErrorKey] as? String,
let data = reason.data(using: .utf8),
let payload = serverPayload(from: data) {
return payload
for key in [NSLocalizedFailureReasonErrorKey, "body", "responseBody", "data"] {
if let value = error.userInfo[key] as? String,
let data = value.data(using: .utf8),
let payload = serverPayload(from: data) {
return payload
} else if let valueData = error.userInfo[key] as? Data,
let payload = serverPayload(from: valueData) {
return payload
}
}
for (_, value) in error.userInfo {
if let str = value as? String,
str.trimmingCharacters(in: .whitespacesAndNewlines).hasPrefix("{"),
let data = str.data(using: .utf8),
let payload = serverPayload(from: data) {
return payload
}
}
return nil
}