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
 }
11
  • 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. Commented Apr 27, 2019 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 Commented Apr 27, 2019 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)) Commented Apr 27, 2019 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. Commented Apr 27, 2019 at 18:54
  • I apologize: There is a main key in the JSON. city is related to the forecast (unlike weather) API Commented Apr 27, 2019 at 18:59

3 Answers 3

25

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.

Sign up to request clarification or add additional context in comments.

2 Comments

This should be the answer! Thanks for taking the time for such a comprehensive response.
@grow4gaurav Excellent reply. Very helpful to pinpoint the actual error message. It helped me a lot. I appreciate it :)
2

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

2 Comments

Oh wait, the city property shouldn't be there, it should be name instead.
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.
-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"
}
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.