0

I am trying to parse some JSON in Swift that I was previously parsing in Objective-C and am having some difficulties.

In objective-C I was able to parse it simply enough using:

NSError* error;
NSDictionary *jsonResults = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
NSNumber *temp = jsonResults[@"main"][@"temp"];
NSNumber *humidity = jsonResults[@"main"][@"humidity"];

In Swift, my code so far is giving errors when I try to serialize into a dictionary or, alternatively, if I serialize intoa string when I try to access the values in the JSON.

What is the proper way to do this in Swift. Here is version where I try to to serialize into Dictionary and it gives an error

  //assemble url query items
    components.queryItems = queryItems
    let url = components.url
    let task = URLSession.shared.dataTask(with: url!) { //open 2
    [weak self] data, response, error in
    print("heard back from task")
    guard let data = data, error == nil else { return }
    do {
    let jsonResults = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as! [Dictionary:Any]
    //Gives error Dictionary' requires that 'Value' conform to 'Hashable'
    let main = jsonResults["main"] as Any
    let temp = main[3]
    completion("got here")
    } catch let error as NSError {
    print(error)
    }

Here is a sample of the JSON:

{"coord":{"lon":10.73,"lat":59.91},"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04d"}],"base":"stations","main":{"temp":49.62,"feels_like":43.45,"temp_min":48,"temp_max":52,"pressure":987,"humidity":30},"wind":{"speed":1.99,"deg":95,"gust":7},"clouds":{"all":95},"dt":1589387530,"sys":{"type":3,"id":2009047,"country":"NO","sunrise":1589337830,"sunset":1589398989},"timezone":7200,"id":3143242,"name":"Oslo County","cod":200}
zztop
  • 701
  • 1
  • 7
  • 20
  • have a look over this, it might help you https://stackoverflow.com/questions/46271889/objective-c-nsdictionary-parsing-nested-json – Yaseen Majeed May 13 '20 at 17:57

1 Answers1

2

In swift you only need the below code: -

Model:

struct Model: Codable {
    let coord: Coord
    let weather: [Weather]
    let base: String
    let main: Main
    let wind: Wind
    let clouds: Clouds
    let dt: Int
    let sys: Sys
    let timezone, 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, feelsLike: Double
    let tempMin, tempMax, pressure, humidity: Int

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

struct Sys: Codable {
    let type, id: Int
    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, gust: Int
}

Parsing:

do {
    let model = try JSONDecoder().decode(Model.self, from: data)
} catch {
    print(error.localizedDescription)
}
Frankenstein
  • 15,732
  • 4
  • 22
  • 47
  • I got a wierd statement in the console There is no data , I think from JSONDecoder().decode – zztop May 14 '20 at 12:44
  • Should the decder take json as an argument (with extra step to serialize data into JSON? – zztop May 14 '20 at 13:13
  • @zztop you need to give Model not Main. The naming in the son was a bit confusing. Check my answer now I've updated. – Frankenstein May 14 '20 at 13:17
  • I think this is close. But what type should data be? My variable data is actually NSData coming from an URL Session and I have to convert it to JSON with something like: let data: Data // received from a network request, for example let json = try? JSONSerialization.jsonObject(with: data, options: [])let jsonData = JSON.data(using: .utf8)! let blogPost: BlogPost = try! JSONDecoder().decode(BlogPost.self, from: jsonData) – zztop May 14 '20 at 13:50
  • It looks like in order to use Decodable, the argument should be in the form of a string cast as data using utf8 as in this example in the Docs: let json = BUNCH OF JSON.data(using: .utf8)! let decoder = JSONDecoder() let product = try decoder.decode(GroceryProduct.self, from: json) https://developer.apple.com/documentation/foundation/jsondecoder – zztop May 14 '20 at 13:53
  • Just provide the data directly. You don't need to do anything. Just provide the data you receive in the completion of URLSession data task which is given as `Data` type. – Frankenstein May 14 '20 at 13:53
  • Give the code I've given for parsing after the line `guard let data = data, error == nil else { return }`. – Frankenstein May 14 '20 at 13:56
  • I put your code do { let model = try JSONDecoder().decode(Model.self, from: data) } catch { print(error.localizedDescription) }immediately after return}. It compiles but the following message appears in the console: The data couldn’t be read because it isn’t in the correct format – zztop May 14 '20 at 14:03
  • Could you update your question by posting what you are doing above this line `guard let data = data, error == nil else { return }` – Frankenstein May 14 '20 at 14:07
  • I appreciate your trying to help me this and will mark it right. However, so far although it compiles it is having a problem with the data input. Added the earlier code in the question – zztop May 14 '20 at 14:24
  • I found more questions on that error message and it seems to result from some slight error in the Structs (such as punctuation or capitalization) or malformation of the JSON itself. I'll play around with the structs and see if I can get it to work. Marked answer correct. Thx! – zztop May 14 '20 at 14:40
  • Found issue. tempMin and tempMax should be doubles.... Thanks again – zztop May 14 '20 at 17:15
  • One more thing to anyone doing this. Use print(error) instead of print(error.localizedDescription) to see the variables actually causing "The data couldn't be read because it isn't in the correct format" error. – zztop May 14 '20 at 17:38