Swift 3 Parsing JSON nesting issue

Hi, I'm trying to parse some json data received and I'm struggling to understand how to pull out and assign the nested values. I can manage the first two levels but when it comes any further I struggle.

I have an updateUI function which receives the data as String: AnyObject which works fine along with the first 2 levels of nesting.

What I'm trying to parse is the "forecastday" "day" array so that I can update the UILabels.


The current function is:

func updateUI(weatherData: [String: AnyObject]) {
     
        let currentWeather = weatherData["current"]!
        let forecast = weatherData["forecast"]!
        let forecastDay = forecast["forecastday"] as AnyObject
     
        let day = forecastDay["day"] as Any
     
        print(day)
     
        DispatchQueue.main.async {
            self.windDirectionLabel.text = (currentWeather["wind_dir"] as! String)
        }
}


My json data is:


{
   "current":{
      "last_updated_epoch":1484042228,
      "last_updated":"2017-01-10 09:57",
      "temp_c":6.0,
      "temp_f":42.8,
      "is_day":1,
      "condition":{
         "text":"Partly cloudy",
         "icon":"//cdn.apixu.com/weather/64x64/day/116.png",
         "code":1003
      },
      "wind_mph":23.0,
      "wind_kph":37.1,
      "wind_degree":290,
      "wind_dir":"WNW",
      "pressure_mb":1005.0,
      "pressure_in":30.2,
      "precip_mm":0.6,
      "precip_in":0.02,
      "humidity":93,
      "cloud":0,
      "feelslike_c":0.8,
      "feelslike_f":33.5
   },
   "forecast":{
      "forecastday":[
         {
            "date":"2017-01-10",
            "date_epoch":1484006400,
            "day":{
               "maxtemp_c":8.1,
               "maxtemp_f":46.6,
               "mintemp_c":5.7,
               "mintemp_f":42.3,
               "avgtemp_c":5.9,
               "avgtemp_f":42.6,
               "maxwind_mph":22.4,
               "maxwind_kph":36.0,
               "totalprecip_mm":1.0,
               "totalprecip_in":0.04,
               "condition":{
                  "text":"Cloudy",
                  "icon":"//cdn.apixu.com/weather/64x64/day/119.png",
                  "code":1006
               }
            }
            
          }]
          }
     }
} // The rest ommitted to preseerve space :)


Any help would be great.

Thanks

Replies

I'm struggling to understand how to pull out and assign the nested values.

Are you just trying to parse the JSON? Or do you need to assign values within the JSON? The latter is trickier, so I’m going to assume you’re interested in the former.

With regards the former, the approach I usually use is to grind down each level of the JSON, casting values to the right type as I go. For example:

import Foundation

let jsonData = try! JSONSerialization.data(withJSONObject: [
    "forecast": [
        "forecastday": [
            [
                "day": [
                    "maxtemp_c": 1.2,
                    "maxtemp_f": 3.4
                ]
            ],
            [
                "day": [
                    "maxtemp_c": 5.6,
                    "maxtemp_f": 7.8
                ]
            ]
        ]
    ]
], options: .prettyPrinted)
let jsonRoot = try! JSONSerialization.jsonObject(with: jsonData)
let rootDict = jsonRoot as! [String:Any]
let forecast = rootDict["forecast"] as! [String:Any]
let forecastdays = forecast["forecastday"] as! [[String:Any]]
let forecastday0 = forecastdays[0]
let day = forecastday0["day"] as! [String:AnyObject]
let maxTemp = day["maxtemp_c"] as! Double
print(maxTemp)

Obviously this isn’t appropriate for production code (all those exclamation marks will crash if something goes wrong) but it should be enough to get you up’n’limping.

Parsing JSON is the canonical ‘introduction to Swift’ question, and you’ll find as many opinions on this as you will Swift programmers. Personally I lean towards the approach outlined in Working with JSON in Swift. It’s a bit long-winded, but it’s really easy to understand.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

This works for me in playgrounds.


typealias JSONDictionary = [String: Dictionary<String, Any>]
typealias JSONArray = [JSONDictionary]

let jsonRoot = try! JSONSerialization.jsonObject(with: jsonData) as? JSONDictionary
let forecasts = jsonRoot?["forecast"]?["forecastday"] as? JSONArray

if let forecasts = forecasts {

  for forecast in forecasts {
       let temp = forecast["day"]?["maxtemp_c"]
       if let temp = temp {
            print("Max Temp: \( temp )")
       }
  }

}


minor edit: made types start with caps.