The data couldn’t be read because it isn’t in the correct format (swift)

Viewed 1024

I want to take weather informations with JSON but there is an error : The data couldn’t be read because it isn’t in the correct format.

Error 'It looks like your post is mostly code; please add some more details.' in stackoverflow. Although I briefly describe my problem, it still expects an explanation from me :/

override func viewDidLoad() {
            super.viewDidLoad()
            let url = "https://api.openweathermap.org/data/2.5/weather?q=bursa,tr&appid=00f63a1cff271776651468c0204c422c"
            getData(from: url)
        }
        
        private func getData (from url : String){
           let task = URLSession.shared.dataTask(with: URL(string: url)!, completionHandler: { data , response , error in
                guard let data = data ,  error == nil else {
                    print ("birşeyler ters gitti")
                    return
                }
            var main : Response?
                do {
                    main = try JSONDecoder().decode(Response.self , from: data)
                } catch{
                    print ("ERROR IS HERE!!! \(error.localizedDescription)")
                }
                guard let json = main else {
                    return
                }
            print (json.weather)
                })
            task.resume()
        
        
        
    }}
        
    struct Response : Codable {
               let weather : myResult
               let status : String
               
           }
    struct myResult : Codable {
               let main : String
               let description : String
               let icon : String
           }

API Response is like that :

{"coord": { "lon": 139,"lat": 35},
  "weather": [
    {
      "id": 800,
      "main": "Clear",
      "description": "clear sky",
      "icon": "01n"
    }
  ],
  "base": "stations",
  "main": {
    "temp": 281.52,
    "feels_like": 278.99,
    "temp_min": 280.15,
    "temp_max": 283.71,
    "pressure": 1016,
    "humidity": 93
  },
  "wind": {
    "speed": 0.47,
    "deg": 107.538
  },
  "clouds": {
    "all": 2
  },
  "dt": 1560350192,
  "sys": {
    "type": 3,
    "id": 2019346,
    "message": 0.0065,
    "country": "JP",
    "sunrise": 1560281377,
    "sunset": 1560333478
  },
  "timezone": 32400,
  "id": 1851632,
  "name": "Shuzenji",
  "cod": 200
}
1 Answers

First, error.localizedDescription is meant to display an information for the user. It's not useful for debugging. If you replace it with error:

} catch {
    print ("ERROR IS HERE!!! \(error)") // <- remove .localizedDescription
}

you will get more details:

ERROR IS HERE!!! typeMismatch(Swift.Dictionary<Swift.String, Any>, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "weather", intValue: nil)], debugDescription: "Expected to decode Dictionary<String, Any> but found an array instead.", underlyingError: nil))

To solve this you need to declare weather as an array:

let weather: [myResult]

I'd also recommend replacing myResult with Weather (or at least capitalised MyResult) as it will be more readable:

struct Weather: Codable {
    let main: String
    let description: String
    let icon: String
}

Also, in the JSON response you provided there is no status field so you may need to remove it from the Response class (or make it optional).

If you'd like to add more fields to the response, declare them according to your JSON structure. Eg. if you want to add humidity and temperature you can do:

struct Response: Codable {
    ...
    let main: Main
}

struct Main: Codable {
    let temp: Double
    let humidity: Double
}

To have a more readable code you can use CodingKeys - then your variable names can be independent from JSON variables.

struct Main: Codable {
    enum CodingKeys: String, CodingKey {
        case temperature = "temp"
        case humidity
    }
    
    let temperature: Double
    let humidity: Double
}

Summing up, your Response may look like this:

struct Response: Codable {
    let weather: [Weather]
    let main: Main
    // alternatively declare `status` optional
    // let status: String?
}

struct Weather: Codable {
    let main: String
    let description: String
    let icon: String
}

struct Main: Codable {
    enum CodingKeys: String, CodingKey {
        case temperature = "temp"
        case humidity
    }
    
    let temperature: Double
    let humidity: Double
}
Related