1
votes

Can someone tell what's wrong here please?

It send me this error: Type 'CityWeatherInfo' does not conform to protocol 'Encodable'


struct CityWeatherInfo: Codable {
   var name: String
   var main: Main
   var weathers: [Weather]

   private enum CodingKeys: String, CodingKey {
       case weathers = "weather"
       case main = "main"
       case name

   }
   init(from decoder: Decoder) throws {
       let container = try decoder.container(keyedBy: CodingKeys.self)
       self.name = try container.decode(String.self, forKey: .name)
       let mainContainer = try container.nestedContainer(keyedBy: CodingKeys.self, forKey: .main)
       let weatherContainer = try container.nestedContainer(keyedBy: CodingKeys.self, forKey: .weathers)
   }
}

struct Weather: Decodable {
   var main: String
   var description: String

   private enum WeatherKeys: String, CodingKey {
       case main = "main"
       case description = "description"
   }
}

struct Main: Decodable {
   var temp: Double
   var feels_like: Double
   var temp_min: Double
   var temp_max: Double

   private enum MainKeys: String, CodingKey {
       case temp = "temp"
       case feels_like = "feels_like"
       case temp_min = "temp_min"
       case temo_max = "temp_max"
   }
}

Json is this:

{"coord":{"lon":-0.13,"lat":51.51},"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04n"}],"base":"stations","main":{"temp":287.45,"feels_like":286.61,"temp_min":284.82,"temp_max":289.15,"pressure":1012,"humidity":72},"visibility":10000,"wind":{"speed":1,"deg":0},"clouds":{"all":100},"dt":1592362322,"sys":{"type":1,"id":1414,"country":"GB","sunrise":1592365362,"sunset":1592425222},"timezone":3600,"id":2643743,"name":"London","cod":200}

1

1 Answers

2
votes

If a struct or class adopts Codable the required methods init(from decoder: Decoder) and encode(to encoder: Encoder) are synthesized by default.

But if you implement one of the methods yourself you have to implement the other, too. Or – in this case – adopt only Decodable because you are only reading/decoding the data.

There are a few bad practices in your code.

  • The custom CodingKeys WeatherKeys and MainKeys are pointless because in the default syntax the framework generates the keys named CodingKeys. Custom keys are considered only in a custom implementation.

  • Both nestedContainers are not needed and they make no sense anyway if they are keyed by the same CodingKeys.

  • You can omit the CodingKeys if the dictionary keys match the struct member names.

  • If the values of the struct members are not going to be modified declare them as constants (let).

  • According to the naming convention variables are supposed to be named lowerCamelCased. A convenient way to translate snake_case to camelCase is to specify the convertFromSnakeCase key decoding strategy.


The openweathermap API provides degrees in Celsius by adding units=metric in the URL or Fahrenheit by adding units=imperial. For example

https://api.openweathermap.org/data/2.5/weather?q=London&units=metric&appid=•••••••••••••"

The UNIX timestamps (1592362322) can be decoded as Date by specifying the secondsSince1970 date decoding strategy

The structs can be created in this simple form

struct CityWeatherInfo: Decodable {
   let name: String
   let main: Main
   let weather: [Weather]
   let dt : Date
}

struct Weather: Decodable {
   let main: String
   let description: String
}

struct Main: Decodable {
   let temp, tempMin, tempMax, feelsLike : Double
}

And decode the data with this code

 do {
     let decoder = JSONDecoder()
     decoder.keyDecodingStrategy = .convertFromSnakeCase
     decoder.dateDecodingStrategy = .secondsSince1970
     let weatherInfo = try decoder.decode(CityWeatherInfo.self, from: data)
     print(weatherInfo)
 } catch {
    print(error)
 }