keyNotFound(CodingKeys(stringValue: “coord”, intVa

2019-09-23 04:18发布

问题:

I am building a small swift weather app using the openweatherAPI and I am running into some issues trying to parse the JSON. I have used the following function to parse the get and parse the json.

Below is my weather data struct:

struct WeatherData: Codable {
    let coord: Coord
    let weather: [Weather]
    let base: String
    let main: Main
    let visibility: Int
    let wind: Wind
    let clouds: Clouds
    let dt: Int
    let sys: Sys
    let id: Int
    let name: String
    let cod: Int
}

struct Clouds: Codable {
    let all: Int
}

struct Coord: Codable {
    let lon, lat: Double
}

struct Main: Codable {
    let temp: Double
    let pressure, humidity: Int
    let tempMin, tempMax: Double

    enum CodingKeys: String, CodingKey {
        case temp, pressure, humidity
        case tempMin = "temp_min"
        case tempMax = "temp_max"
    }
}

struct Sys: Codable {
    let type, id: Int
    let message: Double
    let country: String
    let sunrise, sunset: Int
}

struct Weather: Codable {
    let id: Int
    let main, description, icon: String
}

struct Wind: Codable {
    let speed: Double
    let deg: Int
}


private func getWeatherData(url: String, parameters: [String : String]) {
    let JsonURLString:[String: Any] = ["url": WEATHER_URL, "parameters": parameters]
    print(JsonURLString)
    let urlString = JsonURLString["url"] as? String
    guard let url = URL(string: urlString!) else { return }
    URLSession.shared.dataTask(with: url) { ( data, response, err ) in
        DispatchQueue.main.sync {
            if let err = err {
                print("Failed to get data from url:", err)
                return
            }
            guard let data = data else { return }
            do {
                let decoder = JSONDecoder()
                decoder.keyDecodingStrategy = .convertFromSnakeCase
                let city = try decoder.decode(WeatherData.self, from: data)
                self.weatherData.description = city.weather[0].description
                self.weatherData.temperature = Int(city.main.temp - 273)
                self.weatherData.city = city.name
                self.weatherData.condition = city.weather[0].id
                self.updateUIWeatherData()
            } catch {
                print(error)
                self.cityLabel.text = "Connection issues"
            }
        }
    }.resume()
}

The exact error I am getting is the following:

  longitude = -0.1337, latitude = 51.50998
 ["parameters": ["lat": "51.50998", "long": "-0.1337", "appid":    "xxxxxxxxxxxxxxxxxx"], "url":   "https://api.openweathermap.org/data/2.5/weather"]
  keyNotFound(CodingKeys(stringValue: "coord", intValue: nil),    Swift.DecodingError.Context(codingPath: [], debugDescription: "No value associated     with key CodingKeys(stringValue: \"coord\", intValue: nil) (\"coord\").",    underlyingError: nil))

I have looked at the following example and don't see how this would apply. Any help would be appreciated.

Icon is not appearing. Here is my model:

import UIKit

class WeatherDataModel {

//Declare your model variables here
var temperature: Int = 0
var condition: Int = 0
var city: String = ""
var weatherIconName = ""
var description: String = ""

//This method turns a condition code into the name of the weather condition image

func updateWeatherIcon(condition: Int) -> String {

switch (condition) {

    case 0...300 :
        return "tstorm1"

    case 301...500 :
        return "light_rain"

    case 501...600 :
        return "shower3"

    case 601...700 :
        return "snow4"

    case 701...771 :
        return "fog"

    case 772...799 :
        return "tstorm3"

    case 800 :
        return "sunny"

    case 801...804 :
        return "cloudy2"

    case 900...903, 905...1000  :
        return "tstorm3"

    case 903 :
        return "snow5"

    case 904 :
        return "sunny"

    default :
        return "dunno"
    }

   }
 }

I have added my own icons. I have added this in the do catch block.

do {
                let decoder = JSONDecoder()
                decoder.keyDecodingStrategy = .convertFromSnakeCase
                let city = try decoder.decode(WeatherData.self, from: data)
                print(city)
                self.weatherData.description = city.weather[0].description
                self.weatherData.temperature = Int(city.main.temp - 273)
                self.weatherData.city = city.name
                self.weatherData.condition = city.weather[0].id
                self.weatherData.weatherIconName = WeatherDataModel.updateWeatherIcon(self.weatherData.condition)
                self.updateUIWeatherData()
            } catch {
                print(error)
                self.cityLabel.text = "Connection issues"
            }

The error I am getting this error now:

Instance member 'updateWeatherIcon' cannot be used on type 'WeatherDataModel'; did you mean to use a value of this type instead?

回答1:

You are creating only the openweathermap URL but you ignore the parameters.

Use something like this for example URLComponents and URLQueryItem to build the URL query properly

private func getWeatherData(parameters: [String : String]) {
    guard let lat = parameters["lat"], 
          let long = parameters["long"],
          let appID = parameters["appid"] else { print("Invalid parameters"); return } 
    var urlComponents = URLComponents(string: "https://api.openweathermap.org/data/2.5/weather")!
    let queryItems = [URLQueryItem(name: "lat", value: lat),
                      URLQueryItem(name: "lon", value: long),
                      URLQueryItem(name: "appid", value: appID)]
    urlComponents.queryItems = queryItems

    guard let url = urlComponents.url else { return }
    URLSession.shared.dataTask(with: url) { ( data, response, err ) in
        DispatchQueue.main.async { // never, never, never sync !!
            if let err = err {
                print("Failed to get data from url:", err)
                return
            }
            guard let data = data else { return }
            do {
                let decoder = JSONDecoder()
                decoder.keyDecodingStrategy = .convertFromSnakeCase
                let city = try decoder.decode(WeatherData.self, from: data)
                print(city)
                self.weatherData.description = city.weather[0].description
                self.weatherData.temperature = Int(city.main.temp - 273)
                self.weatherData.city = city.name
                self.weatherData.condition = city.weather[0].id
                self.updateUIWeatherData()
            } catch {
                print(error)
                self.cityLabel.text = "Connection issues"
            }
        }
        }.resume()
}

and pass only

["lat": "51.50998", "long": "-0.1337", "appid": "xxxxxxxxxxxxxxxxxx"]

as parameters.