I have a function which I use to make api call but sometimes, I could get a decaying error and debugging this has been difficult. Below is my function, is there any way to maybe log or print the bad decoding key since I am using combine.
func request<T>(with builder: BaseRequest, customDecoder: JSONDecoder) -> AnyPublisher<T, APIError> where T: Codable {
let encoding: ParametersEncoder = [.get, .delete].contains(builder.method) ? URLParameretersEncoder() : JSONParametersEncoder()
customDecoder.keyDecodingStrategy = .convertFromSnakeCase
var url: URL {
var components = URLComponents()
components.scheme = "http"
components.host = builder.baseUrl
components.path = "/api/v1" builder.path
guard let url = components.url else {
preconditionFailure("Invalid URL components: \(components)")
}
return url
}
var urlRequest = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalAndRemoteCacheData, timeoutInterval: 46.0)
urlRequest.httpMethod = builder.method.rawValue
builder.headers.forEach { key, value in
urlRequest.setValue(value, forHTTPHeaderField: key)
}
if let token = tokenManager.token {
urlRequest.setValue("Bearer " token, forHTTPHeaderField: "Authorization")
}
if let parameters = builder.parameters {
guard let encoded = try? encoding.encode(parameters: parameters, in: urlRequest) else {
fatalError()
}
urlRequest = encoded
}
self.log(request: urlRequest)
return URLSession.shared
.dataTaskPublisher(for: urlRequest)
.receive(on: DispatchQueue.main)
.mapError { _ in .unknown }
.flatMap { data, response -> AnyPublisher<T, APIError> in
guard let response = response as? HTTPURLResponse else {
return Fail(error: APIError.invalidResponse).eraseToAnyPublisher()
}
self.log(response: response, data: data, error: nil)
if (200...299).contains(response.statusCode) {
return Just(data)
.decode(type: T.self, decoder: customDecoder)
// .map {
// print($0)
// return $0
// } //added
.mapError {_ in .decodingError}
.eraseToAnyPublisher()
} else {
guard let errorResponse = try? customDecoder.decode(BaseResponse.self, from: data) else {
return Fail(error: APIError.decodingError).eraseToAnyPublisher()
}
return Fail(error: APIError.server(response: errorResponse))
.eraseToAnyPublisher()
}
}
.eraseToAnyPublisher()
}
CodePudding user response:
I have been using this snippet to point to the bad decoding key which might help.
if let data = response.data {
do {
let decoder = JSONDecoder()
let messages = try decoder.decode(T.self, from: data)
print(messages as Any)
} catch DecodingError.dataCorrupted(let context) {
print(context)
} catch DecodingError.keyNotFound(let key, let context) {
print("Key '\(key)' not found:", context.debugDescription)
print("codingPath:", context.codingPath)
} catch DecodingError.valueNotFound(let value, let context) {
print("Value '\(value)' not found:", context.debugDescription)
print("codingPath:", context.codingPath)
} catch DecodingError.typeMismatch(let type, let context) {
print("Type '\(type)' mismatch:", context.debugDescription)
print("codingPath:", context.codingPath)
} catch {
print("error: ", error)
}
}
Note that only include it for debugging purposes if decoding an API is not working as expected, and remove it after you have made necessary changes after seeing its output.