skip to Main Content

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


  1. 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.

    struct WeatherView : View {
        @State var post: Post?
        var body: some View{
                 return VStack {
                     Text("lol")
                 }
                 .task {
                     do {
                         try await WeatherApi().getPost()
                     } catch {
                         print(error)
                     }
                 }
    
    
            //return VStack{
             //   Text("Unable to retrieve data from API Server")
            //}.onAppear()
        }
    }
    
    class WeatherApi {
        func getPost() async throws -> Post? {
    
            guard let url = URL(string: "https://api.weather.yandex.ru/v2/forecast?lat=50&lon=37") else {
                return nil
            }
            let (data, _) = try await URLSession.shared.data(from: url)
            let result = try JSONDecoder().decode(Post.self, from: data)
    
            print(result)
    
            return result
        }
    }
    
    Login or Signup to reply.
  2. 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 use convertFromSnakeCase as it’s KeyDecodingStrategy or you can setup CodingKeys.

    Some debugging tips you can use to help figure out wants going wrong is:

    1. Add a breakpoint on the line where you attempting to decode the data.
    2. You can print the data out in a human readable way in the Xcode console with print String(data: data, encoding: .utf8)
    3. You can get info about the decoding process by running print JSONDecoder().decode(Post.self, from: data!) in the Xcode console.
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search