When I try to decode my JSON in SwiftUI it always gives an error:
keyNotFound(CodingKeys(stringValue: "is_paid", intValue: nil)
Some values like Double
do seem to work but other types seems to give these type of errors. I have been searching for this answer for over 2 days now and I'm breaking my head over this.
struct Invoices: Decodable, Hashable, Identifiable {
let id: Int
let is_paid: Int
let total: Double
let invoice_id: String
}
Somehow id and total give the correct value:
success([KlantenTest.InvoiceTest(id: 22, total: 30.0), KlantenTest.InvoiceTest(id: 26, total: 29.9959), KlantenTest.InvoiceTest(id: 28, total: 363.0), KlantenTest.InvoiceTest(id: 31, total: 240.1608), KlantenTest.InvoiceTest(id: 32, total: 29.9959)])
func getCategorByAPI() -> Void {
let url = URL(string: "\(self.Api_URL)/api/v1/webhook/invoices/\(self.Api_token)/\(self.UserToken ?? self.Default)")!
var request = URLRequest(url: url)
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
request.setValue("Bearer \(self.Token ?? "")", forHTTPHeaderField: "Authorization")
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
URLSession.shared.jsonDecodableTask(with: request, decoder: decoder) { (result: Result<[InvoiceTest], Error>) in
print(result)
switch result {
case .success(let person):
print("InvoiceID: \(person)")
case .failure(let error):
print(error)
}
}.resume()
}
enum URLError: Error {
case noData, decodingError
}
extension URLSession {
/// A type safe URL loader that either completes with success value or error with Error
func jsonDecodableTask<T: Decodable>(with url: URLRequest, decoder: JSONDecoder = JSONDecoder(), completion: @escaping (Result<T, Error>) -> Void) -> URLSessionDataTask {
self.dataTask(with: url) { (data, response, error) in
DispatchQueue.main.async {
guard error == nil else {
completion(.failure(error!))
return
}
guard let data = data, let _ = response else {
completion(.failure(URLError.noData))
return
}
do {
let decoded = try decoder.decode(T.self, from: data)
completion(.success(decoded))
} catch {
completion(.failure(error))
}
}
}
}
func jsonDecodableTask<T: Decodable>(with url: URL, decoder: JSONDecoder = JSONDecoder(), completion: @escaping (Result<T, Error>) -> Void) -> URLSessionDataTask {
self.jsonDecodableTask(with: URLRequest(url: url), decoder: decoder, completion: completion)
}
}
[
{
"id": 28,
"invoice_id": "20210126075159",
"total": 363,
"is_paid": 1,
},
{
"id": 31,
"invoice_id": "20210126075161",
"total": 240.1608,
"is_paid": 1,
},
{
"id": 32,
"invoice_id": "20210126075162",
"total": 29.9959,
"is_paid": 1,
}
]
As you can see the values are actually there so I don't understand where the error is coming from because when I try function below, it DOES work with the same model Invoices
let Url = URL(string: "\(self.Api_URL)/api/v1/webhook/invoices/\(self.Api_token)/\(self.UserToken ?? self.Default)")!
var request = URLRequest(url: Url)
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
request.setValue("Bearer \(self.Token ?? "")", forHTTPHeaderField: "Authorization")
URLSession.shared.dataTask(with: request) { (data, res, err) in
//check response status and err
guard let data = data else { return }
do {
let invoice = try JSONDecoder().decode([Invoices].self, from: data)
self.invoices = invoice
} catch {
print("Failed to decode: \(error)")
}
}.resume()
PS. I also tried to call it through [GetInvoices]
struct GetInvoices: Decodable {
let invoices: [Invoices]
}
But that can't even find the id or the total that Invoices
can find, so that's no use.
Code used from: https://stackoverflow.com/a/59838319/5100123
Your problem is in: .convertFromSnakeCase
As stated in reference: https://developer.apple.com/documentation/foundation/jsondecoder/keydecodingstrategy/convertfromsnakecase
is_paid
is being decoded into isPaid
, same for invoice_id
.