I am trying to get information about weather via API, request works perfectly, i am able to get JSON data. But for some reason URLSession gets skipped and therefore leaving nil value in data-storing variable post.
Attempt to display info:
var body: some View{
TabView {
WeatherView()
.tabItem {
Image(systemName: "cloud.rain")
.font(.largeTitle)
Text("Weather")
}
}
}
struct WeatherView : View {
@State var post: Post?
var body: some View{
return VStack {
Text("lol")
}
.onAppear(){
WeatherApi().getPost{ (post) in
self.post = post
}
}
//return VStack{
// Text("Unable to retrieve data from API Server")
//}.onAppear()
}
}
class WeatherApi {
func getPost(completion: @escaping (Post) -> ()){
guard let url = URL(string: "https://api.weather.yandex.ru/v2/forecast?lat=50&lon=37") else {return}
print("kek")
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.setValue("[key here]", forHTTPHeaderField: "X-Yandex-API-Key")
URLSession.shared.dataTask(with: request){(data, _, _) in
let post = try! JSONDecoder().decode(Post.self, from: data!)
print(post)
DispatchQueue.main.async {
completion(post)
}
}
.resume()
}
}
struct Post : Codable, Identifiable {
let id = UUID()
let now: Int
let now_dt: String
let info: info
let geo_object: geo_object
let yesterday: yesterday
let fact: fact
let forecasts: [forecast]
}
// MARK: - fact
struct fact : Codable, Identifiable {
let id = UUID()
let obs_time, uptime, temp, feels_like: Int
let icon: icon
let condition: condition
let cloudness, prec_type, prec_prob, prec_strength: Int
let is_thunder: Bool
let wind_speed: Double
let wind_dir: wind_dir
let pressure_mm, pressure_pa, humidity: Int
let daytime: daytime
let polar: Bool
let season, source: String
let accum_prec: [String: Double]
let soil_moisture: Double
let soil_temp, uv_index, wind_gust: Int
}
enum condition: String, Codable {
case clear
case cloudy
case light_rain
case overcast
case partly_cloudy
}
enum daytime:String, Codable {
case d
case n
}
enum icon: String, Codable {
case bkn_d
case bkn_n
case ovc
case ovc_ra
case skc_d
case skc_n
}
enum wind_dir: String, Codable {
case n
case ne
case s
case sw
case w
}
// MARK: - Forecast
struct forecast: Codable, Identifiable {
let id = UUID()
let date: String
let date_ts, week: Int
let sunrise, sunset, rise_begin, set_end: String
let moon_code: Int
let moon_text: String
let parts: parts
let hours: [hour]
let biomet: biomet?
}
// MARK: - biomet
struct biomet: Codable, Identifiable {
let id = UUID()
let index: Int
let condition: String
}
// MARK: - Hour
struct hour: Codable, Identifiable {
let id = UUID()
let hour: String
let hour_ts, temp, feels_like: Int
let icon: icon
let condition: condition
let cloudness: Double
let prec_type, prec_strength: Int
let is_thunder: Bool
let wind_dir: wind_dir
let wind_speed, wind_gust: Double
let pressure_mm, pressure_pa, humidity, uv_index: Int
let soil_temp: Int
let soil_moisture: Double
let prec_mm, prec_period, prec_prob: Int
}
// MARK: - Parts
struct parts: Codable, Identifiable {
let id = UUID()
let morning, night, day, day_short: day
let night_short, evening: day
}
// MARK: - Day
struct day: Codable, Identifiable {
let id = UUID()
let source: String
let temp_min, temp_avg, temp_max: Int?
let wind_speed, wind_gust: Double
let wind_dir: String
let pressure_mm, pressure_pa, humidity, soil_temp: Int
let soil_moisture, prec_mm: Double
let prec_prob, prec_period: Int
let cloudness: Double
let prec_type: Int
let prec_strength: Double
let icon: icon
let condition: condition
let uv_index: Int?
let feels_like: Int
let daytime: daytime
let polar: Bool
let fresh_snow_mm: Int
let temp: Int?
}
// MARK: - gej_object
struct geo_object: Codable, Identifiable {
let id = UUID()
let district:String
let locality, province, country: country
}
// MARK: - country
struct country: Codable, Identifiable {
let id: Int
let name: String
}
// MARK: - info
struct info: Codable, Identifiable {
let id = UUID()
let n: Bool
let geoid: Int
let url: String
let lat, lon: Int
let tzinfo: tzinfo
let def_pressure_mm, def_pressure_pa: Int
let slug: String
let zoom: Int
let nr, ns, nsr, p: Bool
let f, h: Bool
}
// MARK: - Tzinfo
struct tzinfo: Codable, Identifiable {
let id = UUID()
let name, abbr: String
let dst: Bool
let offset: Int
}
// MARK: - yesterday
struct yesterday: Codable, Identifiable {
let id = UUID()
let temp: Int
}
I managed to figure out that it is URLSession.shared.dataTask that does not parse the JSON data in objects but I am not sure what to do next.
UPD: There were multiple problems with camel case, such as windGust instead of wind_gust in JSON. I changed all fields that could cause that problem but now I get another mistake:
hread 5: Fatal error: ‘try!’ expression unexpectedly raised an error: Swift.DecodingError.keyNotFound(CodingKeys(stringValue: "h", intValue: nil), Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "info", intValue: nil)], debugDescription: "No value associated with key CodingKeys(stringValue: "h", intValue: nil) ("h").", underlyingError: nil))
The changed structures are attached
UPD2: Server response is following: a String? "{"now":1682087274,"now_dt":"2023-04-21T14:27:54.524102Z","info":{"n":true,"geoid":20741,"url":"https://yandex.ru/pogoda/20741?lat=10u0026lon=10","lat":10,"lon":10,"tzinfo":{"name":"Africa/Lagos","abbr":"WAT","dst":false,"offset":3600},"def_pressure_mm":713,"def_pressure_pa":950,"slug":"20741","zoom":10,"nr":true,"ns":true,"nsr":true,"p":false,"f":true,"h":false},"geo_object":{"district":null,"locality":null,"province":{"id":20741,"name":"Баучи"},"country":{"id":20741,"name":"Нигерия"}},"yesterday":{"temp":37},"fact":{"obs_time":1682085600,"uptime":1682087274,"temp":38,"feels_like":38,"icon":"ovc-ra","condition":"light-rain","cloudness":1,"prec_type":1,"prec_prob":90,"prec_strength":0.25,"is_thunder":false,"wind_speed":3.9,"wind_dir":"nw","pressure_mm":708,"pressure_pa":943,"humidity":32,"daytime":"d","polar":false,"season":"spring","source":"station","soil_moisture":0.11,"soil_temp":42,"uv_index":2,"wind_gust":6.6},"forecasts":[{"date":"2023-04-21","date_ts":1682031600,"week":16,"sunrise":"06:0"
Xcode does not allow to show more than that
2
Answers
I would recommend making it async. With that, you can debug better. (See below). It looks like you are not setting the api key in the url. Check the documentation for how to do that.
As already mentioned in the comments on your original post, it looks like the issue is that you are trying to map values with mismatching keys.
If the backend is responding with snake case (snake_case) keys but your app is expecting camel case (camelCase) you can either set the
JSONDecoder
to useconvertFromSnakeCase
as it’sKeyDecodingStrategy
or you can setup CodingKeys.Some debugging tips you can use to help figure out wants going wrong is:
print String(data: data, encoding: .utf8)
print JSONDecoder().decode(Post.self, from: data!)
in the Xcode console.