Hello my name is Nico,
I am a complete beginner in App programming/SwiftUI. I am trying to parse json data from an web api but somehow I cannot parse the data correctly. I assume that my json structure is not correct but I cannot find the problem.
The Json which I get from the Web API looks something like this:
{
"pois": [
{
"id": "2635094451",
"lat": "52.410150",
"lat_s": "52.4",
"lng": "10.776630",
"lng_s": "10.8",
"street": "Röntgenstraße",
"content": "8137285512",
"backend": "0-239283152",
"type": "1",
"vmax": "50",
"counter": "0",
"create_date": "2021-11-18 13:21:50",
"confirm_date": "2021-11-18 13:21:43",
"gps_status": "-",
"info": " {\"qltyCountryRoad\":1,\"confirmed\":\"0\",\"gesperrt\":\"0\",\"precheck\":\"[Q1|21|0]\"}",
"polyline": ""
}
],
"grid": []
}
My Structure looks like this:
struct APIResponse: Codable {
let pois: [InputDataPois]
let grid: [InputDataGrid]
private enum CodingKeys: String, CodingKey {
case pois
case grid
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.pois = try container.decode(APIResponse.self, forKey: .pois).pois
self.grid = try container.decode(APIResponse.self, forKey: .grid).grid
}
}
struct InputDataPois: Codable, Identifiable {
let id:String
let lat:String
let lat_s:String
let lng:String
let lng_s:String
let street:String
let content:String
let backend:String
let type:String
let vmax:String
let counter:String
let create_date:String
let confirm_date:String
let gps_status:String
let info:String
let polyline:String
}
extension InputDataPois {
private enum CodingKeys: String, CodingKey {
case id
case lat
case lat_s
case lng
case lng_s
case street
case content
case backend
case type
case vmax
case counter
case create_date
case confirm_date
case gps_status
case info
case polyline
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.id = try container.decode(String.self, forKey: .id)
self.lat = try container.decode(String.self, forKey: .lat)
self.lat_s = try container.decode(String.self, forKey: .lat_s)
self.lng = try container.decode(String.self, forKey: .lng)
self.lng_s = try container.decode(String.self, forKey: .lng_s)
self.street = try container.decode(String.self, forKey: .street)
self.content = try container.decode(String.self, forKey: .content)
self.backend = try container.decode(String.self, forKey: .backend)
self.type = try container.decode(String.self, forKey: .type)
self.vmax = try container.decode(String.self, forKey: .vmax)
self.counter = try container.decode(String.self, forKey: .counter)
self.create_date = try container.decode(String.self, forKey: .create_date)
self.confirm_date = try container.decode(String.self, forKey: .confirm_date)
self.gps_status = try container.decode(String.self, forKey: .gps_status)
self.info = try container.decode(String.self, forKey: .info)
self.polyline = try container.decode(String.self, forKey: .polyline)
}
}
struct InputDataGrid: Codable {
}
and my Bundle like this:
extension Bundle {
func decode(_ file: String) -> [InputDataPois] {
// 1. Locate the Json File
guard let url = URL(string: file) else {
fatalError("Failed to locate \(file) in bundle")
}
// 2.Create a property for the Data
guard let data = try? Data(contentsOf: url) else {
fatalError("Failed to Load \(file) from bundle.")
}
// 3. Create a property for the data
let str = String(decoding: data, as: UTF8.self)
print("\(str)")
guard let loaded = try? JSONDecoder().decode(APIResponse.self, from: data).pois else {
fatalError("Failed to decode \(file) from bundle.")
}
// 4. Return the ready-to-use data
return loaded
}
}
And In my View I am using:
let speed: [InputDataPois] = Bundle.main.decode("https://cdn2.atudo.net/api/1.0/vl.php?type=0,1,2,3,4,5,6&box=52.36176390234046,10.588760375976562,52.466468685912744,11.159706115722656")
The error I am getting looks something like this.
Thanks in advance for you help.
Remove your custom Codable implementation and coding keys. They are not necessary, the compiler can generate them for you with this simple JSON. Then everything should work.
The problem here specifically is your APIResponse decoding:
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.pois = try container.decode(APIResponse.self, forKey: .pois).pois
self.grid = try container.decode(APIResponse.self, forKey: .grid).grid
}
To decode an APIResponse you try to decode two APIResponses. This again would try to decode APIResponse leading to endless recursion. The only reason you are not seeing that is that your JSON contains arrays making the second call to container(keyedBy:)
fail. Try setting a breakpoint (or if you must a print statement) on the first line of your init(from:)
and you will see that this is called a second time before it fails.
To fix that you would need to decode the actual type of your properties:
self.pois = try container.decode([InputDataPois].self, forKey: .pois)
But as I said, there is nothing in your JSON format requiring manually implementing the decoder, so the best is to let the compiler synthesize it for you.