没有使用OpenWeather API

时间:2019-05-08 10:40:55

标签: ios swift api nsurlsession

我刚刚开始学习SwiftiOS,但是我的天气应用程序存在问题。我正在尝试获取天气数据并在控制台中打印,但是仍然没有任何内容,我也不知道可能是什么问题。我尝试更改模型,粘贴了所有网址,但仍然无效。有时应用会发送数据而没有答案,但是在其他时间,答案是或没有发送,没有答案。在控制台中始终为零...

生成的模型类

    import Foundation

    class CurrentWeather : NSObject, NSCoding{


    var base : String!
    var clouds : Cloud!
    var cod : Int!
    var coord : Coord!
    var dt : Int!
    var id : Int!
    var main : Main!
    var name : String!
    var sys : Sy!
    var visibility : Int!
    var weather : [Weather]!
    var wind : Wind!


    /**
     * Instantiate the instance using the passed dictionary values to set the properties values
     */
    init(fromDictionary dictionary: [String:Any]){
        base = dictionary["base"] as? String
        cod = dictionary["cod"] as? Int
        dt = dictionary["dt"] as? Int
        id = dictionary["id"] as? Int
        name = dictionary["name"] as? String
        visibility = dictionary["visibility"] as? Int
        if let cloudsData = dictionary["clouds"] as? [String:Any]{
            clouds = Cloud(fromDictionary: cloudsData)
        }
        if let coordData = dictionary["coord"] as? [String:Any]{
            coord = Coord(fromDictionary: coordData)
        }
        if let mainData = dictionary["main"] as? [String:Any]{
            main = Main(fromDictionary: mainData)
        }
        if let sysData = dictionary["sys"] as? [String:Any]{
            sys = Sy(fromDictionary: sysData)
        }
        if let windData = dictionary["wind"] as? [String:Any]{
            wind = Wind(fromDictionary: windData)
        }
        weather = [Weather]()
        if let weatherArray = dictionary["weather"] as? [[String:Any]]{
            for dic in weatherArray{
                let value = Weather(fromDictionary: dic)
                weather.append(value)
            }
        }
    }

    /**
     * Returns all the available property values in the form of [String:Any] object where the key is the approperiate json key and the value is the value of the corresponding property
     */
    func toDictionary() -> [String:Any]
    {
        var dictionary = [String:Any]()
        if base != nil{
            dictionary["base"] = base
        }
        if cod != nil{
            dictionary["cod"] = cod
        }
        if dt != nil{
            dictionary["dt"] = dt
        }
        if id != nil{
            dictionary["id"] = id
        }
        if name != nil{
            dictionary["name"] = name
        }
        if visibility != nil{
            dictionary["visibility"] = visibility
        }
        if clouds != nil{
            dictionary["clouds"] = clouds.toDictionary()
        }
        if coord != nil{
            dictionary["coord"] = coord.toDictionary()
        }
        if main != nil{
            dictionary["main"] = main.toDictionary()
        }
        if sys != nil{
            dictionary["sys"] = sys.toDictionary()
        }
        if wind != nil{
            dictionary["wind"] = wind.toDictionary()
        }
        if weather != nil{
            var dictionaryElements = [[String:Any]]()
            for weatherElement in weather {
                dictionaryElements.append(weatherElement.toDictionary())
            }
            dictionary["weather"] = dictionaryElements
        }
        return dictionary
    }

    /**
     * NSCoding required initializer.
     * Fills the data from the passed decoder
     */
    @objc required init(coder aDecoder: NSCoder)
    {
        base = aDecoder.decodeObject(forKey: "base") as? String
        clouds = aDecoder.decodeObject(forKey: "clouds") as? Cloud
        cod = aDecoder.decodeObject(forKey: "cod") as? Int
        coord = aDecoder.decodeObject(forKey: "coord") as? Coord
        dt = aDecoder.decodeObject(forKey: "dt") as? Int
        id = aDecoder.decodeObject(forKey: "id") as? Int
        main = aDecoder.decodeObject(forKey: "main") as? Main
        name = aDecoder.decodeObject(forKey: "name") as? String
        sys = aDecoder.decodeObject(forKey: "sys") as? Sy
        visibility = aDecoder.decodeObject(forKey: "visibility") as? Int
        weather = aDecoder.decodeObject(forKey: "weather") as? [Weather]
        wind = aDecoder.decodeObject(forKey: "wind") as? Wind
    }

    /**
     * NSCoding required method.
     * Encodes mode properties into the decoder
     */
    @objc func encode(with aCoder: NSCoder)
    {
        if base != nil{
            aCoder.encode(base, forKey: "base")
        }
        if clouds != nil{
            aCoder.encode(clouds, forKey: "clouds")
        }
        if cod != nil{
            aCoder.encode(cod, forKey: "cod")
        }
        if coord != nil{
            aCoder.encode(coord, forKey: "coord")
        }
        if dt != nil{
            aCoder.encode(dt, forKey: "dt")
        }
        if id != nil{
            aCoder.encode(id, forKey: "id")
        }
        if main != nil{
            aCoder.encode(main, forKey: "main")
        }
        if name != nil{
            aCoder.encode(name, forKey: "name")
        }
        if sys != nil{
            aCoder.encode(sys, forKey: "sys")
        }
        if visibility != nil{
            aCoder.encode(visibility, forKey: "visibility")
        }
        if weather != nil{
            aCoder.encode(weather, forKey: "weather")
        }
        if wind != nil{
            aCoder.encode(wind, forKey: "wind")
        }
    }

}

天气服务:


    import Foundation

    class WeatherSerice {

    let weatherAPIKey: String
    let weatherBaseURL: URL?

    init(APIKey: String) {

        self.weatherAPIKey = APIKey
        weatherBaseURL = URL(string: "https://api.openweathermap.org/data/2.5/weather?")
    }

    func getCurrentWeather(city: String, completion: @escaping (CurrentWeather?) -> Void) {

        if let weatherURL = URL(string: "\(weatherBaseURL!)q=\(city)&appid=\(weatherAPIKey)") {

            let networkProcessor = NetworkProcessor(url: weatherURL)
            networkProcessor.downloadJSONFromURL({(jsonDictionary) in

            if let currentWeatherDictionary = jsonDictionary?["currently"] as?
                [String : Any] {
                let currentWeather = CurrentWeather(fromDictionary: currentWeatherDictionary)
                completion(currentWeather)
            } else {
                completion(nil)
                }
            })
        }
    }
}

UIViewController的一部分

  let weatherService = WeatherSerice(APIKey: "52e6ff60bba8613b4850e065dcd3d0ac")
    weatherService.getCurrentWeather(city: "London") {
        (currentWeather) in

        print (currentWeather)
    }

1 个答案:

答案 0 :(得分:1)

请把NSCoding换成Codable,您将摆脱所有难看的样板代码

struct WeatherData : Decodable {
    let coord : Coordinate
    let cod, visibility, id : Int
    let name : String
    let base : String
    let weather : [Weather]
    let clouds: Clouds
    let sys : Sys
    let main : Main
    let wind : Wind
    let dt : Date
}

struct Coordinate : Decodable {
    let lat, lon : Double
}

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

struct Sys : Decodable {
    let type, id : Int
    let sunrise, sunset : Date
    let message : Double
    let country : String
}

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

struct Wind : Decodable {
    let speed : Double
    let deg : Int
    let gust : Double?
}

struct Clouds: Decodable {
    let all: Int
}

enum MainEnum: String, Decodable {
    case clear = "Clear"
    case clouds = "Clouds"
    case rain = "Rain"
}

如您所见,日期被解码为Date,而main中的Weather值被解码为枚举。


我不知道API NetworkProcessor,因此我将其替换为传统的URLSession。添加的URLComponents提供了正确的URL编码,例如,如果city包含空格字符

import Foundation

class WeatherSerice {

    let weatherAPIKey: String
    let weatherBase = "https://api.openweathermap.org/data/2.5/weather"

    init(APIKey: String) {

        self.weatherAPIKey = APIKey
    }

    func getCurrentWeather(city: String, completion: @escaping (WeatherData?) -> Void) {

        var urlComponents = URLComponents(string: weatherBase)!
        let queryItems = [URLQueryItem(name: "q", value: city),
                         URLQueryItem(name: "appid", value: weatherAPIKey)]                     
        urlComponents.queryItems = queryItems

        if let weatherURL = urlComponents.url {

            let task = URLSession.shared.dataTask(with: weatherURL) { (data, response, error) in

                if let error = error { print(error); completion(nil) }

                do {
                    let decoder = JSONDecoder()
                    decoder.keyDecodingStrategy = .convertFromSnakeCase
                    decoder.dateDecodingStrategy = .secondsSince1970
                    let result = try decoder.decode(WeatherData.self, from: data!)
                    completion(result)  
                } catch { 
                    print(error)
                    completion(nil)
                }       
            }
            task.resume()
        }
    }
}

我也建议在Swift 5中使用新的Result类型来返回错误。

    func getCurrentWeather(city: String, completion: @escaping (Result<WeatherData,Error>) -> Void) {

        var urlComponents = URLComponents(string: weatherBase)!
        let queryItems = [URLQueryItem(name: "q", value: city),
                         URLQueryItem(name: "appid", value: weatherAPIKey)]                     
        urlComponents.queryItems = queryItems

        if let weatherURL = urlComponents.url {

            let task = URLSession.shared.dataTask(with: weatherURL) { (data, response, error) in

                if let error = error { completion(.failure(error)) }

                do {
                    let decoder = JSONDecoder()
                    decoder.keyDecodingStrategy = .convertFromSnakeCase
                    decoder.dateDecodingStrategy = .secondsSince1970
                    let result = try decoder.decode(WeatherData.self, from: data!)
                    completion(.success(result))
                } catch { 
                    completion(.failure(error))
                }       
            }
            task.resume()
        }

并使用它

weatherService.getCurrentWeather(city: "London") { result in
    switch result {
    case .success(let result): print(result)
    case .failure(let error): print(error)
    }
}