3

I have been learning about JSON parsing in SWIFT apps. So far I have been using a simple free API and had no issues, application worked as designed. But now I am calling an API that returns an array inside an array. I have read and prepared my variables to access the element as instructed, but still SWIFT does not see any data inside this returned JSON. I have been struggling with this for 2 days, changing all I can find online and think of, still my default variables are not being overwritten with the data from JSON. Using the pod SWIFTYJson.

JSON partial extract:

    {
  "list" : [
    {
      "main" : {
        "grnd_level" : 984.67999999999995,
        "temp_min" : 283.30000000000001,
        "temp_max" : 284.54599999999999,
        "temp" : 283.30000000000001,
        "sea_level" : 1022.5,
        "pressure" : 984.67999999999995,
        "humidity" : 88,
        "temp_kf" : -1.25
      },
      "clouds" : {
        "all" : 0
      },
      "weather" : [
        {
          "main" : "Clear",
          "icon" : "01n",
          "description" : "clear sky",
          "id" : 800
        }
      ],

Code to handle it:

func getWeatherData(url: String, parameters: [String : String]){
    //make http request and handle the JSON response
    print("\(url)\(parameters)")
    Alamofire.request(url, method: .get, parameters: parameters).responseJSON {
        response in //in means you are inside a closure (function inside a function)
        if response.result.isSuccess {
            print("Successfully got the weather data!")

            let weatherJSON : JSON = JSON(response.result.value!) //saving the JSON response to a constant weathrJSON  . We are using ! to self unwrap the value.
            self.updateWeatherData(json: weatherJSON) //func to parse our json from API. Self tells the compiler to look for the method inside the current class

            print(weatherJSON)
        } else {
            self.cityName.text = "Unable to fetch weather - please check active connection."
        }

    }
}

func updateWeatherData(json: JSON) {
            (...)
             //parse JSON for weather forecast day 1
            weatherDataModel.weatherTest1 = json["list"][0]["weather"][0]["id"].intValue

            //parse JSON for weather forecast day 2
            weatherDataModel.weatherTest2 = json["list"][8]["weather"][0]["id"].intValue

            //parse JSON for weather forecast day 3
            weatherDataModel.weatherTest3 = json["list"][16]["weather"][0]["id"].intValue

            print("TEST variable 1: \(weatherDataModel.weatherTest1)")
            print("TEST variable 2: \(weatherDataModel.weatherTest2)")
            print("TEST variable 3: \(weatherDataModel.weatherTest3)")

Variables printed to console remain unchanged from default values:

TEST variable 1: 0
TEST variable 2: 0
TEST variable 3: 0

3 Answers 3

1

You parsing data by wrong way.

json["list"] is just JSON object not array object, So how can you pass index to by using [0].

This should be corrected as below solution.

weatherDataModel.weatherTest1 = json["list"].arrayValue[0]["weather"].arrayValue[0]["id"].intValue
Sign up to request clarification or add additional context in comments.

2 Comments

With the code above the app is crashing for me atm, Thread 1: Fatal error: Index out of range. I will investigate further, thanks.
@M.Spoldi this happens because may be your array have no value you can use safe checking index.
0

In the current version of SwiftyJSON the notes on the initializer you are using says this:

/**
 Creates a JSON object
 - note: this does not parse a `String` into JSON, instead use `init(parseJSON: String)`

 - parameter object: the object

 - returns: the created JSON object
 */

So instead of let weatherJSON : JSON = JSON(response.result.value!), you should instead use let weatherJSON : JSON = JSON(parseJSON: response.result.value!)

After that I think your current code should work, based on your JSON.

Edit:

I know the other answer was chosen that says that json["list"] is a JSON object and not an array, but the JSON indicated in the extract clearly says:

"list" : [
    {

The [ is an indication that it is an array, so I'm not sure what was wrong in the first place. I'm leaving this answer here in case someone does encounter the same issue and uses init(JSON) to parse an object with the current version of SwiftyJSON.

Comments

0

If you are learning how to parse in Swift, you must learn how to construct a Model to parse correctly your JSON.

(By the way, if you are in Swift 4, no need to use SWIFTYJson, learn more here : https://benscheirman.com/2017/06/swift-json/)

If you want to use SWIFTYJson, an example :

struct WeatherDataModel {
    var list = [ListModel]?

    init() {
        self.list = []
    }

    init(json: [String: Any]) {
        if let object = json["list"] as? [[String: Any]] {
            for listJson in object {
                let list = ListModel(json: listJson)
                self.list.append(list)
            }
        }
    }
}

struct ListModel {
    var main : Main?
    var clouds : Clouds?
    var weather : [Weather]?
    init() {
        self.main = Main()
        self.clouds = Clouds()
        self.weather = []
    }
    init(json: [String: Any]) {
        if let mainJson = json["main"] as? [String: Any] {
            self.main = MainModel(json: mainJson)
        }
        if let cloudsJson = json["clouds"] as? [String: Any] {
            self.clouds = CloudsModel(json: cloudsJson)
        }
        if let objectWeather = json["weather"] as? [[String: Any]] {
            for weatherJson in objectWeather {
                let weather = WeatherModel(json: weatherJson)
                self.weather.append(weather)
            }
        }
    }
}

Do the same with MainModel, CloudsModel, WeatherModel

After you got all your models, you can parse it in your request:

Alamofire.request(url, method: .get, parameters: parameters).validate().responseJSON { (response) -> Void in
        if let value = response.data {
            do {
                let json = try JSON(data: data)
                if let dictionnary = json.dictionnaryObject {
                    let object = WeatherDataModel(json: dictionnary)
                    self.updateWeatherData(data: object)
                }
            } catch {
                print("cannot convert to Json")
            }
        }
 }

Then you will find in you method the Data object already parsed :

func updateWeatherData(data: WeatherDataModel) {
    // exemple
    print(data.list[0].weather[0].id) // = 800
}

Hope it will help you.

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.