5

currently working through an app that gets and decodes data from OpenWeatherMap API, currently I've got everything working except getting the decoder to return something. Currently, the decoder is returning nil, however, I am getting bytes of data from the API call. I am not exactly sure what could be the issue. I've got the ViewModel struct set up in terms of hierarchy. The OPW API JSON data seems to be in the format of a dictionary key:value pair collection type, keys are enclosed in quotes, could it be that my decoder isn't finding the necessary information because of the quotation marks?

Getting and Decoding the API call...

@IBAction func saveCityButtonPressed() {

    if let city = cityNameTextField.text {
        let weatherURL = URL(string: "https://api.openweathermap.org/data/2.5/weather?q=\(city)&APPID=8bad8461edbaf3ff50aa2f2fd8ad8a71&units=imperial")!

        let weatherResource = Resource<WeatherViewModel>(url: weatherURL) { data in
            let weatherVM = try? JSONDecoder().decode(WeatherViewModel.self, from: data)
            return weatherVM
        }
        Webservice().load(resource: weatherResource) { result in
        }
    }
}

ViewModel

struct WeatherListViewModel {
private var weatherViewModels = [WeatherViewModel]()
}

struct WeatherViewModel: Decodable {
let name: String
let main: TemperatureViewModel
}

struct TemperatureViewModel: Decodable {
let temp: Double
let temp_min: Double
let temp_max: Double
}

Example of JSON data:

{
    "coord":{
       "lon":-0.13,
       "lat":51.51
    },
    "weather":[
        {
             "id":300,
             "main":"Drizzle",
             "description":"light intensity drizzle","icon":"09d"
        }
    ],
    "base":"stations",
    "main":{
        "temp":280.32,
        "pressure":1012,
        "humidity":81,
        "temp_min":279.15,
        "temp_max":281.15
     },
     "visibility":10000,
     "wind":{
         "speed":4.1,
         "deg":80
     },
     "clouds":{
         "all":90
     },
     "dt":1485789600,
     "sys":{
         "type":1,
         "id":5091,
         "message":0.0103,
         "country":"GB",
         "sunrise":1485762037,
         "sunset":1485794875
     },
     "id":2643743,
     "name":"London",
     "cod":200
 }
syds
  • 322
  • 3
  • 12
  • 3
    To say `let weatherVM = try? JSONDecoder().decode(WeatherViewModel.self, from: data)` is just silly. Instead, use `try`, wrap this in a do/catch construct, _catch_ the error, and print it out! Then you will see instantly what the problem is, because a huge honking detailed error message will tell you. – matt Apr 27 '19 at 18:49
  • 2
    Don't `try?`. **Never** `try?` when decoding JSON with `Decodable`. **`catch`** the error, it tells you exactly what's wrong. Hint: There is no `city` and `main` key in the root object – vadian Apr 27 '19 at 18:50
  • 1
    And here it is. This is useful information that you are foolishly, willfully just throwing away! `Swift.DecodingError.keyNotFound(CodingKeys(stringValue: "city", intValue: nil), Swift.DecodingError.Context(codingPath: [], debugDescription: "No value associated with key CodingKeys(stringValue: \"city\", intValue: nil) (\"city\").", underlyingError: nil))` – matt Apr 27 '19 at 18:52
  • Thanks matt and vadian, I'm gonna try that right now. I'm following a lecture project, and this was an issue I had, good thing I came onto SO for help. – syds Apr 27 '19 at 18:54
  • I apologize: There is a `main` key in the JSON. `city` is related to the `forecast` (unlike `weather`) API – vadian Apr 27 '19 at 18:59
  • my mistake, the city property should be name property instead, I made the edit. however, I'm still getting nil with the decoder, im really scratching my head on this one. – syds Apr 27 '19 at 19:08
  • Once again, catch the error – vadian Apr 27 '19 at 19:11
  • I'm going to be honest, not exactly sure how to write the do/catch in this situation, can't figure out what type to return – syds Apr 27 '19 at 19:15
  • `do { let weatherVM = try JSONDecoder()... return weatherVM } catch { print(error) }` – vadian Apr 27 '19 at 19:20
  • thanks vadian, i've already tried that, but i get the error message "Missing return in a closure expected to return 'WeatherViewModel?'" – syds Apr 27 '19 at 19:33
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/192496/discussion-between-sherman-and-vadian). – syds Apr 27 '19 at 20:22

3 Answers3

24

By making the result of JSONDecoder().decode an optional (try?), you are ensuring that you get nil if the decoding goes wrong. You can catch decoding related issues quickly by implementing proper catch blocks. E.g.:

do {
    let decoder = JSONDecoder()
    let messages = try decoder.decode(WeatherViewModel.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)
}

Not a direct answer to your question, but surely will reduce other's time to understand which part of decoding is going wrong.

grow4gaurav
  • 3,145
  • 1
  • 11
  • 12
  • 4
    This should be the answer! Thanks for taking the time for such a comprehensive response. – Scooter Nov 09 '20 at 02:59
  • @grow4gaurav Excellent reply. Very helpful to pinpoint the actual error message. It helped me a lot. I appreciate it :) – sugarakis Jan 16 '23 at 02:44
2

Your WeatherViewModel property city is a String, but there is no "city" key in your JSON.

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • Oh wait, the city property shouldn't be there, it should be name instead. – syds Apr 27 '19 at 18:56
  • my mistake, it shouldn't be city, the property is suppose to be name. I was playing with the code and forgot to change it back, however, even with the name property, I am still getting nil, I'm gonna write up the do/catch block and see what the error message is. – syds Apr 27 '19 at 18:58
-1

Why do we get nil value, when decoding the value?

Reasons:
The response parameter may be the first letter as capital.

Solution:
The coding keys concept is to out to nil value.

Example:

struct Example{
var a: string
var b: string

enum Codingkeys: String,CodingKey{
case a = "a"
case b = "b"
}
}
benson23
  • 16,369
  • 9
  • 19
  • 38
krishnamurthy
  • 59
  • 1
  • 4