Cruder
Cruder

Reputation: 39

Swift - How to decode a JSON path that looks like this 0.lat

I would like to get the position data of a city from the openweathermap Geocoding API But I have a problem with there Json struct and that the path starts with a 0

like this: 0.lat

here is an example of the JSON I get back

[
  {
    "name": "Osimo",
    "local_names": {
      "ru": "Озимо",
      "fr": "Osime",
      "it": "Osimo"
    },
    "lat": 43.4861359,
    "lon": 13.4824068,
    "country": "IT",
    "state": "Marche"
  }
]

And this is my code. I found CodingKey when I was looking up the problem but it doesn't work in my code or I did made a mistake.

PositionData.swift

enum CodingKeys: String, CodingKey {
    case zero = "0"
}
struct PositionData: Decodable {
        let zer0: Zero
    }

struct Zero: Decodable {

But when I would like to code the path it tells me that: Value of type 'PositionData' has no member '0'

LocationManager.Swift

do {
            let decodedData = try decoder.decode(PositionData.self, from: positionData)
            print(decodedData.0.lat)
        } catch {
            print("Error in Json")

I tried to load the position data of the openweathermap Geocoding API. Now I am not sure how to decode the JSON data with a path like this 0.lat

Upvotes: 0

Views: 117

Answers (2)

devdchaudhary
devdchaudhary

Reputation: 718

You could specifically send only the data at position 0 to the json decoder or your could fetch and decode the entire array and show only the data at position 0, it's really upto you.

Client

class APIManager {

static let shared = APIManager()
    
    func fetchWeatherData(url: URL, completionHandler: @escaping ([PositionData]?, Error?) -> Void) {
        let task = URLSession.shared.dataTask(with: url, completionHandler: { data, response, error in
            if let data {
                do {
                    let json = try? JSONSerialization.jsonObject(with: data, options: .fragmentsAllowed) as? [String: Any]
                    if let json = json {
                        completionHandler([PositionData(json)], nil)
                    }
                }
            } else {
                completionHandler(nil, error)
            }
        })
        task.resume()
    }
}

Model

struct PositionData: Decodable {
    
    var id: UUID
    var lat: Double
    var lon: Double
    
    init(_ data: [String : Any]) {
        id = UUID()
        lat = data["lat"] as? Double ?? 0
        lon = data["lon"] as? Double ?? 0
    }
}

View

struct MyView: View {
    
    let url = URL(string: "www.weatherData.com")
    @State var weatherData: [PositionData]?
    
    var body: some View {
        VStack {
            if let weatherData {
                if !weatherData.isEmpty {
                    Text("lat: \(weatherData[0].lat), lon: \(weatherData[0].lon)")
                }
            }
        }
        .onAppear {
            if let url {
                fetchWeather(url: url)
            }
        }
    }
}

ViewModel

extension MyView {
    func fetchWeather(url: URL) {
        APIManager.shared.fetchWeatherData(url: url) { data, error  in
            if let data {
                for datum in data {
                    weatherData.append(datum)
                }
            } else {
                print(error?.localizedDescription)
            }
        }
    }
}

Upvotes: 1

vadian
vadian

Reputation: 285072

There is no the path starts with a 0.

You have to decode an array and get the item at index zero

do {
    let decodedData = try decoder.decode([PositionData].self, from: positionData)
     print(decodedData[0].lat)  // better print(decodedData.first?.lat)
} catch {
    print(error) // NEVER print a literal like ("Error in Json")
}

and delete

enum CodingKeys: String, CodingKey {
    case zero = "0"
}
struct PositionData: Decodable {
        let zer0: Zero
    }

struct Zero: Decodable {

PositionData must look like

struct PositionData: Decodable {
    let lat, lon: Double
    // ...
}

Upvotes: 1

Related Questions