2

I am currently trying to make a weather app using JSON from https://openweathermap.org but I am having trouble with the weather part in the JSON file. I am not sure how to access the 'id' value inside the object.

{
  "base": "stations",
  "clouds": {
    "all": 36
  },
  "cod": 200,
  "coord": {
    "lat": 51.51,
    "lon":
      -0.13
  },
  "dt": 1507497600,
  "id": 2643743,
  "main": {
    "humidity": 82,
    "pressure": 1021,
    "temp": 10.65,
    "temp_max": 13,
    "temp_min": 9
  },
  "name": "London",
  "sys": {
    "country": "GB",
    "id": 5091,
    "message": 0.0036,
    "sunrise": 1507443264,
    "sunset": 1507483213,
    "type": 1
  },
  "visibility": 10000,
  "weather": [{
    "description": "scattered clouds",
    "icon": "03n",
    "id": 802,
    "main": "Clouds"
  }],
  "wind": {
    "deg": 200,
    "speed": 1.5
  }
}

How would I be able to get the data in there. In my swift code, I am using structures that conform to the new 'codable' protocol in swift 4.

// all structures for the data
struct CurrentLocalWeather: Codable {
    let base: String
    let clouds: clouds
    let cod: Int
    let coord: coord
    let dt: Int
    let id: Int
    let main: main
    let name: String
    let sys: sys
    let visibility: Int
    let weather: [weather]
    let wind: wind
    }  

struct clouds: Codable {
    let all: Int
}

struct coord: Codable {
    let lat: Double
    let lon: Double
}

struct main: Codable {
    let humidity: Double
    let pressure: Double
    let temp: Double
    let temp_max: Double
    let temp_min: Double
}

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

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

struct wind: Codable {
    let deg: Double
    let speed: Double
}

// Get data from weather server
func getCurrentWeatherData() {

        let jsonUrlString = "https://api.openweathermap.org/data/2.5/weather?id=2643743&units=metric&APPID=fdf04e9483817ae2fa77048f7e6705e8"

        guard let url = URL(string: jsonUrlString) else { return }

        URLSession.shared.dataTask(with: url) { (data, response, err) in

            guard let data = data else { return }

            do {
                let decoder = JSONDecoder()
                decoder.dateDecodingStrategy = .iso8601

                let json = try decoder.decode(CurrentLocalWeather.self, from: data)

                print("Data Successfully Retrieved!\nServer Response: \(json.cod)\nLocation: \(json.name)")

                DispatchQueue.main.async() {
                    // Any of the following allows me to access the data from the JSON 
                    self.locationLabel.text = "\(json.name)"
                    self.temperatureLabel.text = "Currently: \(json.main.temp)ºC"
                    self.highTemperatureLabel.text = "High: \(json.main.temp_max)ºC"
                    self.lowTemperatureLabel.text = "Low: \(json.main.temp_min)ºC"
                    self.sunriseLabel.text = "\(self.convertFromUnixToNormal(time: json.sys.sunrise))"
                    self.sunsetLabel.text = "\(self.convertFromUnixToNormal(time: json.sys.sunset))"
                    self.humidityLabel.text = "Humidity: \(json.main.humidity)%"
                    self.pressureLabel.text = "Pressure: \(json.main.pressure) hPa"
                    self.windSpeedLabel.text = "Wind Speed: \(json.wind.speed) km/h"


                }
            } catch let jsonErr {
                print("Error: \(jsonErr)")
            }

        }.resume()
    }

2 Answers 2

5

You need to properly identify your json string and provide all necessary structures to decode it. Just by looking at the json provided you can have an idea of the necessary structs to decode it properly:

struct CurrentLocalWeather: Codable {
    let base: String
    let clouds: Clouds
    let cod: Int
    let coord: Coord
    let dt: Int
    let id: Int
    let main: Main
    let name: String
    let sys: Sys
    let visibility: Int
    let weather: [Weather]
    let wind: Wind
}
struct Clouds: Codable {
    let all: Int
}
struct Coord: Codable {
    let lat: Double
    let lon: Double
}
struct Main: Codable {
    let humidity: Int
    let pressure: Int
    let temp: Double
    let tempMax: Int
    let tempMin: Int
    private enum CodingKeys: String, CodingKey {
        case humidity, pressure, temp, tempMax = "temp_max", tempMin = "temp_min"
    }
}
struct Sys: Codable {
    let country: String
    let id: Int
    let message: Double
    let sunrise: UInt64
    let sunset: UInt64
    let type: Int
}
struct Weather: Codable {
    let description: String
    let icon: String
    let id: Int
    let main: String
}
struct Wind: Codable {
    let deg: Int
    let speed: Double
}

let weatherData = Data("""
{"base"  : "stations",
 "clouds": { "all": 36 },
 "cod"   : 200,
 "coord" : { "lat": 51.51,
             "lon": -0.13},
 "dt": 1507497600,
 "id": 2643743,
 "main": {
        "humidity": 82,
        "pressure": 1021,
        "temp": 10.65,
        "temp_max": 13,
        "temp_min": 9},
 "name": "London",
 "sys": {
        "country": "GB",
        "id": 5091,
        "message": 0.0036,
        "sunrise": 1507443264,
        "sunset": 1507483213,
        "type": 1 },
 "visibility": 10000,
 "weather": [{
    "description": "scattered clouds",
    "icon": "03n",
    "id": 802,
    "main": "Clouds"}],
 "wind": {
    "deg": 200,
    "speed": 1.5
    }
 }
""".utf8)

let decoder = JSONDecoder()

do {
    let currentLocalWeather = try decoder.decode(CurrentLocalWeather.self, from: weatherData)
    print(currentLocalWeather)   // "CurrentLocalWeather(base: "stations", clouds: __lldb_expr_367.Clouds(all: 36), cod: 200, coord: __lldb_expr_367.Coord(lat: 51.509999999999998, lon: -0.13), dt: 1507497600, id: 2643743, main: __lldb_expr_367.Main(humidity: 82, pressure: 1021, temp: 10.65, temp_max: 13, temp_min: 9), name: "London", sys: __lldb_expr_367.Sys(country: "GB", id: 5091, message: 0.0035999999999999999, sunrise: 1507443264, sunset: 1507483213, type: 1), visibility: 10000, weather: [__lldb_expr_367.Weather(description: "scattered clouds", icon: "03n", id: 802, main: "Clouds")], wind: __lldb_expr_367.Wind(deg: 200, speed: 1.5))\n"
} catch {
    print(error)
}
Sign up to request clarification or add additional context in comments.

4 Comments

Thanks. I can get the data and print with no problems but I don't understand how to get the data stored under the object called weather in the JSON file. For example, if I wanted to get the wind speed, I would simply code json.wind.speed but to get the weather id, I can't do json.weather.id because the data is stored in an array with a dictionary. I would like to get the data from here -> weather: [Weather.weather(description: "overcast clouds", icon: "04d", id: 804, main: "Clouds")]
json.weather.first?.whatever
Btw it is not an array of dictionaries it is an array of Weather objects
.first was the missing part! Thank you and sorry for the incorrect terminology🤦‍♂️
1

You need to define types for the custom types in your JSON, e.g. weather, clouds, coord, etc. I would recommend looking at the example in the documentation.

In the example, the Landmark type has a 'location' property which is of Coordinate type. You could even use the Coordinate type in the example for the coord property in your JSON object. However, you will need to provide the correct keys using the CodingKey protocol which is also described in the documentation. For example, your Coordinate type may look like this:

struct Coordinate: Codable {
    var latitude: Double
    var longitude: Double

    enum CodingKeys: String, CodingKey {
        case latitude = "lat"
        case longitude = "lon"
    }
}

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.