I’m trying to decode some JSON data, and I ran into a problem with CodingKeys. I’m able to decode the whole JSON without a problem until I add an enum with coding keys.
The working code looks like this
import Foundation
// Top-level response struct
struct WeatherData: Codable {
let weather: [Weather]
let main: MainWeather
let visibility: Double
let wind: Wind
let clouds: Clouds
let sys: System
let name: String
}
// Weather condition struct (array)
struct Weather: Codable {
let id: Int
let main: String
let description: String
}
// Main weather data struct
struct MainWeather: Codable {
let temp: Double
let pressure: Int
let humidity: Double
}
// Wind data struct
struct Wind: Codable {
let speed: Double
let deg: Int
}
struct Clouds: Codable {
let all: Double
}
// System data struct
struct System: Codable {
let sunrise: TimeInterval
let sunset: TimeInterval
}
It stops decoding when I change the code to this
import Foundation
// Top-level response struct
struct WeatherData: Codable {
let weather: [Weather]
let main: MainWeather
let visibility: Double
let wind: Wind
let clouds: Clouds
let sys: System
let name: String
}
// Weather condition struct (array)
struct Weather: Codable {
let id: Int
let main: String
let description: String
}
// Main weather data struct
struct MainWeather: Codable {
let temp: Double
let pressure: Int
let humidity: Double
let tempMin: Double
let tempMax: Double
let feelsLike: Double
let seaLevel: Double
let grndLevel: Double
// Custom coding keys to map JSON keys with underscores
enum CodingKeys: String, CodingKey {
case temp
case feelsLike = "feels_like"
case tempMin = "temp_min"
case tempMax = "temp_max"
case pressure
case humidity
case seaLevel = "sea_level"
case grndLevel = "grnd_level"
}
}
// Wind data struct
struct Wind: Codable {
let speed: Double
let deg: Int
}
struct Clouds: Codable {
let all: Double
}
// System data struct
struct System: Codable {
let sunrise: TimeInterval
let sunset: TimeInterval
}
My code for decoding looks like this
private let baseURL = URL(string: "https://api.openweathermap.org/data/2.5/weather")!
func fetchWeather(for location: String) async throws -> WeatherData {
var components = URLComponents(url: baseURL, resolvingAgainstBaseURL: false)!
components.queryItems = [
URLQueryItem(name: "q", value: location.lowercased()),
URLQueryItem(name: "appid", value: Constants.apiKey),
URLQueryItem(name: "units", value: "metric")
]
guard let fetchURL = components.url else {
throw FetchError.invalidURL
}
let (data, response) = try await URLSession.shared.data(from: fetchURL)
guard let response = response as? HTTPURLResponse, response.statusCode == 200 else {
throw FetchError.badResponse
}
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let weather = try decoder.decode(WeatherData.self, from: data)
return weather
}
And finally, the JSON data looks like this
{
"coord": {
"lon": 13.3776,
"lat": 49.7475
},
"weather": [
{
"id": 804,
"main": "Clouds",
"description": "overcast clouds",
"icon": "04n"
}
],
"base": "stations",
"main": {
"temp": 283.88,
"feels_like": 283.38,
"temp_min": 282.31,
"temp_max": 285.3,
"pressure": 1025,
"humidity": 91,
"sea_level": 1025,
"grnd_level": 979
},
"visibility": 10000,
"wind": {
"speed": 0.89,
"deg": 245,
"gust": 2.24
},
"clouds": {
"all": 100
},
"dt": 1729444238,
"sys": {
"type": 2,
"id": 20633,
"country": "CZ",
"sunrise": 1729402572,
"sunset": 1729440356
},
"timezone": 7200,
"id": 3068160,
"name": "Pilsen",
"cod": 200
}
>Solution :
It looks to me like you are "double fixing" your key conversion. Either set your keyDecodingStrategy to .convertFromSnakeCase or provide CodingKeys that map your property names to their keys, but not both.