0
votes

I am trying to retrieve the Main and Description form the value but it does not show up in the UILabel. All other parameters show up well. Like Temperature and Max and Min Temp Wind speed and direction. I am also having difficulties with weather icon and need some guidance if my code is right.

import UIKit
import Foundation

class ViewController: UIViewController {
    var timer = Timer()

    struct CurrentLocalWeather: Codable {
        let base: String
        let clouds: Clouds
        let cod: Int
        let coord: Coord
        let dt: Int
        let id: Int
        let main: Main
        let name: String
        let sys: Sys
        let visibility: Int
        let weather: Weather
        let wind: Wind
    }

    struct Clouds: Codable {
        let all: Int
    }

    struct Coord: Codable {
        let lat: Double
        let lon: Double
    }

    struct Main: Codable {
        let humidity: Int
        let pressure: Int
        let temp: Double
        let tempMax: Double
        let tempMin: Double
        private enum CodingKeys: String, CodingKey {
            case humidity, pressure, temp, tempMax = "temp_max", tempMin = "temp_min"
        }
    }

    struct Sys: Codable {
        let country: String
        let id: Int
        let message: Double
        let sunrise: UInt64
        let sunset: UInt64
        let type: Int
    }

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

    struct Wind: Codable {
        let deg: Int
        let speed: Double
    }

    //Variables from Storyboard
    @IBOutlet weak var Temp: UILabel!
    @IBOutlet weak var TempMin: UILabel!
    @IBOutlet weak var TempMax: UILabel!
    @IBOutlet weak var WindSpeed: UILabel!
    @IBOutlet weak var Direction: UILabel!
    @IBOutlet weak var Humidity: UILabel!
    @IBOutlet weak var MainDesc: UILabel!
    @IBOutlet weak var Description: UILabel!
    @IBOutlet weak var City: UILabel!
    @IBOutlet weak var SunRise: UILabel!
    @IBOutlet weak var SunSet: UILabel!
    @IBOutlet weak var Icon: UIImageView!

    override func viewDidLoad() {
        super.viewDidLoad()

        // Define URL for Weather Data
        URLSession.shared.dataTask(with: URL(string: "https://api.openweathermap.org/data/2.5/weather?lat=51.05011&lon=-114.08529&appid=<APPID_REMOVED_FOR_POSTING_PURPOSE>&units=metric")!) { data, response, error in
            if let error = error {
                print("Error:\n\(error)")
            } else {
                do {
                    let JSon = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as! [String: AnyObject]

                    if let Temp1 = JSon["weather"]as? NSDictionary {
                        DispatchQueue.main.async {
                            self.Description.text = Temp1["description"] as? String
                            self.MainDesc.text = Temp1["main"] as? String
                        let Ico = (Temp1["icon"] as? String)!
                            let Icon = UIImageView()
                            let urlStr = NSURL(string:"http://openweathermap.org/img/w/\(Ico).png")
                            let urlData = NSData(contentsOf: urlStr! as URL)
                            if urlData == nil {
                                Icon.image = UIImage(data: urlData! as Data)
                            }
                        }
                    }
} catch let jsonError as NSError {
                    print("JSON error:\n\(jsonError.description)")
                }
            }
            }.resume()
}

This is the result from the query:

{"coord":{"lon":-114.09,"lat":51.05},"weather":[{"id":802,"main":"Clouds","description":"scattered clouds","icon":"03d"}],"base":"stations","main":{"temp":10.9,"pressure":1014,"humidity":32,"temp_min":8.89,"temp_max":13.33},"visibility":56326,"wind":{"speed":11.3,"deg":20},"clouds":{"all":40},"dt":1556142366,"sys":{"type":1,"id":989,"message":0.0068,"country":"CA","sunrise":1556108578,"sunset":1556160342},"id":5913490,"name":"Calgary","cod":200}

2
Why is everything Codable if you are going to use JSONSerialization? In fact, you don't use any of the structs you posted. Don't use NSDictionary or NSArray or NSData or NSURL in Swift. Use proper Swift classes/structs. - rmaddy
And it is standard to start variable, function, and case names with lowercase letters. Class, struct, and enum names start with uppercase. - rmaddy

2 Answers

0
votes

I was able to solve the issue for the string value not showing up by making the below changes.

Import UIKit
import Foundation

class ViewController: UIViewController {
    var timer = Timer()


    //Variables from Storyboard
    @IBOutlet weak var Temp: UILabel!
    @IBOutlet weak var TempMin: UILabel!
    @IBOutlet weak var TempMax: UILabel!
    @IBOutlet weak var WindSpeed: UILabel!
    @IBOutlet weak var Direction: UILabel!
    @IBOutlet weak var Humidity: UILabel!
    @IBOutlet weak var MainDesc: UILabel!
    @IBOutlet weak var Description: UILabel!
    @IBOutlet weak var City: UILabel!
    @IBOutlet weak var SunRise: UILabel!
    @IBOutlet weak var SunSet: UILabel!
    @IBOutlet weak var Icon: UIImageView!

    override func viewDidLoad() {
        super.viewDidLoad()

        // Define URL for Weather Data
        URLSession.shared.dataTask(with: URL(string: "https://api.openweathermap.org/data/2.5/weather?lat=51.05011&lon=-114.08529&appid=<APPID_REMOVED_FOR_POSTING_PURPOSE>&units=metric")!) { data, response, error in
            if let error = error {
                print("Error:\n\(error)")
            } else {
                do {
                    let JSon = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as! [String: AnyObject]

                    let weatherData = (JSon["weather"]as! NSArray)
                    if let Temp1 = weatherData[0] as? NSDictionary {
                        DispatchQueue.main.async {
                            self.Description.text = Temp1["description"] as? String
                            self.MainDesc.text = Temp1["main"] as? String
                        let Ico = (Temp1["icon"] as? String)!
                            let urlStr = NSURL(string:"http://openweathermap.org/img/w/\(Ico).png")
                            let urlData = NSData(contentsOf: urlStr! as URL)
                            if urlData != nil {
                                self.Icon.image = UIImage(data: urlData! as Data)
                            }
                        }
                    }
} catch let jsonError as NSError {
                    print("JSON error:\n\(jsonError.description)")
                }
            }
            }.resume()
}

I am still stuck with getting the icon to work hopefully i should be able to post that as well. for folks who tried using Codable and got it right please share.

0
votes

It's pretty easy with Decodable.

These are the structs omitting the keys you don't want

struct CurrentLocalWeather: Decodable {
    let main: Main
    let name: String
    let sys: Sys
    let weather: [Weather]
    let wind: Wind
}

struct Main: Decodable {
    let humidity: Int
    let temp: Double
    let tempMin, tempMax: Double
}

struct Sys: Decodable {
    let sunrise: Date
    let sunset: Date
}

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

struct Wind: Codable {
    let speed: Double
    let deg: Int
}

To get the icon you need a second data task, retrieving the data synchronously is very bad practice.

And not conforming to the naming convention regarding case sensitivity is very bad practice, too.

class ViewController: UIViewController {
    var timer = Timer()


    //Variables from Storyboard
    @IBOutlet weak var temp: UILabel!
    @IBOutlet weak var tempMin: UILabel!
    @IBOutlet weak var tempMax: UILabel!
    @IBOutlet weak var windSpeed: UILabel!
    @IBOutlet weak var direction: UILabel!
    @IBOutlet weak var humidity: UILabel!
    @IBOutlet weak var mainDesc: UILabel!
    @IBOutlet weak var desc: UILabel!
    @IBOutlet weak var city: UILabel!
    @IBOutlet weak var sunrise: UILabel!
    @IBOutlet weak var sunset: UILabel!
    @IBOutlet weak var icon: UIImageView!

    override func viewDidLoad() {
        super.viewDidLoad()

        // Define URL for Weather Data
        URLSession.shared.dataTask(with: URL(string: "https://api.openweathermap.org/data/2.5/weather?lat=51.05011&lon=-114.08529&appid=<APPID_REMOVED_FOR_POSTING_PURPOSE>&units=metric")!) { data, response, error in
           guard let data = data else {  print("Error:", error!); return }
            do {
               let decoder = JSONDecoder()
               decoder.keyDecodingStrategy = .convertFromSnakeCase
               decoder.dateDecodingStrategy = .secondsSince1970
               let result = try decoder.decode(CurrentLocalWeather.self, from: data)
               guard let weather = result.weather.first else { return }
               DispatchQueue.main.async {
                    self.desc.text = weather.description
                    self.mainDesc.text = weather.main
                    let icon = weather.icon
                    URLSession.shared.dataTask(with: URL(string: "http://openweathermap.org/img/w/\(icon).png")!) { iconData, _ , _ in
                       if let data = iconData {
                          DispatchQueue.main.async {
                             self.icon.image = UIImage(data: data)
                          }
                       }
                    }.resume()
               }
            } catch {
                print("JSON error:", error)
            }
       }.resume()
    }
}