为什么我无法从开放天气 api 接收到任何数据?

Why am I not able to recieve any data from the open weather api?

我正在使用 swift,我想接收温度和湿度的数据,但我收到的是零。我在另一个 swift 文件中有两个对象 temphumdity。我在我的代码中做错了什么?不确定我错过了什么。

struct Weather: Codable {
    var temp: Double?
    var humidity: Int?
    var name : String?
}

struct WeatherMain: Codable {
    let main: Weather
}


ViewController

class ViewController: UIViewController, CLLocationManagerDelegate {
    
    let locationManager = CLLocationManager()
    var latitudeValue = Double()
    var longitudeValue = Double()
    
    @IBOutlet weak var humidityLabel: UILabel!
    
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        guard let locValue: CLLocationCoordinate2D = manager.location?.coordinate else { return }
        print("locations = \(locValue.latitude) \(locValue.longitude)")
        latitudeValue = locValue.latitude
        longitudeValue = locValue.longitude
    }
    
    func retrieve() {
        fetchWeather(lat: latitudeValue, lon: longitudeValue)
        { (response , error ) in
            for res in response! {
                print("Humid value is \(res.humidity ?? 0)")
            }
        }
    }
    
    @IBAction func showData(_ sender: Any) {
        retrieve()
    }
}
extension ViewController {
    
    func fetchWeather(lat: Double, //Required
                      lon: Double,
                      completionHandler: @escaping ([Weather]?, Error?) -> Void) {
        
        // MARK: Retrieve
        let apikey = "45345345345343454Fake API"
        /// create URL
        let baseURL = "https://api.openweathermap.org/data/2.5/weather?lat=\(lat)&lon=\(lon)&appid=\(apikey)"
        let url = URL(string: baseURL)
        print("this is the url for weather : \(url!)")
        /// Creating request
        var request = URLRequest(url: url!)
        request.setValue("Bearer \(apikey)", forHTTPHeaderField: "Authorization")
        request.httpMethod = "GET"
        URLSession.shared.dataTask(with: request) { (data, response, error) in
            if let err = error {
                print(err.localizedDescription)
            }
            do {
                /// Read data as JSON
                let json = try JSONSerialization.jsonObject(with: data!, options: [])
                /// Main dictionary
                guard let resp = json as? NSDictionary else { return }
                /// weather
                guard let weatherDic = resp.value(forKey: "weather") as? [NSDictionary] else { return }
                let weatherData = try? JSONDecoder().decode(WeatherMain.self, from: data!)
                var weatherList: [Weather] = []
                /// Accessing each weather
                for weatherObject in weatherDic {
                    if let weatherData = weatherData {
                        var weather = weatherData.main
                        //print("This is the temp \(weather.temp!)")
                        //print("This is the humidity \(weather.humidity!)")
                        weather.temp = weatherObject.value(forKey: "temp") as? Double
                        weather.humidity = weatherObject.value(forKey: "humidity") as? Int
                        weather.name = weatherObject.value(forKey: "name") as? String
                        weatherList.append(weather)
                    }
                }
                completionHandler(weatherList, nil)
            } catch {
                print("Caught error")
                completionHandler(nil, error)
            }
        }.resume()
    }
}

嗯,据我所知,您的代码中几乎没有错误。

  1. 您将永远不会收到位置更新,因为您没有设置 CLLocationManagerDelegate,您没有请求使用位置的授权,您还没有要求位置管理器开始更新位置。
  2. 您的响应解析代码似乎不正确。您需要学习如何使用 Codable 来解析 JSON 响应。

我对您的代码进行了一些修改,使其可以正常工作。但在复制粘贴之前,请打开您的 Info.plist 并为这些键添加以下键和值。这是重要的一步。

Privacy - Location Usage Description

Privacy - Location When In Use Usage Description

下一步是创建正确的响应模型。要简化创建响应模型的过程,您可以使用网站 https://quicktype.io 以下是网站为该 api 响应生成的内容:

// This file was generated from JSON Schema using quicktype, do not modify it directly.
// To parse the JSON, add this file to your project and do:
//
//   let weatherResponse = try? newJSONDecoder().decode(WeatherResponse.self, from: jsonData)

import Foundation

// MARK: - WeatherResponse
struct WeatherResponse: Codable {
    let coord: Coord?
    let weather: [Weather]?
    let base: String?
    let main: Main?
    let visibility: Int?
    let wind: Wind?
    let clouds: Clouds?
    let dt: Int?
    let sys: Sys?
    let timezone, id: Int?
    let name: String?
    let cod: Int?
}

// MARK: - Clouds
struct Clouds: Codable {
    let all: Int?
}

// MARK: - Coord
struct Coord: Codable {
    let lon, lat: Double?
}

// MARK: - Main
struct Main: Codable {
    let temp, feelsLike, tempMin, tempMax: Double?
    let pressure, humidity: Int?

    enum CodingKeys: String, CodingKey {
        case temp
        case feelsLike = "feels_like"
        case tempMin = "temp_min"
        case tempMax = "temp_max"
        case pressure, humidity
    }
}

// MARK: - Sys
struct Sys: Codable {
    let type, id: Int?
    let country: String?
    let sunrise, sunset: Int?
}

// MARK: - Weather
struct Weather: Codable {
    let id: Int?
    let main, weatherDescription, icon: String?

    enum CodingKeys: String, CodingKey {
        case id, main
        case weatherDescription = "description"
        case icon
    }
}

// MARK: - Wind
struct Wind: Codable {
    let speed, deg: Int?
}

终于更新了 ViewController

import UIKit
import CoreLocation

class ViewController: UIViewController, CLLocationManagerDelegate {


    @IBOutlet weak var humidityLabel: UILabel!
    
    let locationManager = CLLocationManager()
    var location: CLLocation?
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        locationManager.delegate = self // set your CLLocationManagerDelegate to your ViewController instance
        
        checkAuthorizationStatus() // Check current authorization status
    }

    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
 
        location = locations.first
    }
    
    func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
        
        checkAuthorizationStatus()
    }
    
    private func checkAuthorizationStatus() {

        var authorizationStatus: CLAuthorizationStatus!
        
        if #available(iOS 14.0, *) {
            authorizationStatus = locationManager.authorizationStatus
        } else {
            authorizationStatus = CLLocationManager.authorizationStatus()
        }
        
        switch authorizationStatus ?? .notDetermined {
        case .authorizedAlways, .authorizedWhenInUse: // If authorized
            locationManager.startUpdatingLocation() // request updating location
        case CLAuthorizationStatus.denied, CLAuthorizationStatus.restricted: // if denied we are not able to receive location updates
            print("Application doesn't have access to location.")
        case CLAuthorizationStatus.notDetermined: // if not determined we can request authorization
            locationManager.requestWhenInUseAuthorization() // request authorization
        @unknown default:
            print("Unknown authorization status")
        }
    }

    func retrieve() {

        guard let location = location else {
            print("Location is nil.")
            return
        }
        
        fetchWeather(forLocation: location) { [weak self] (result) in

            switch result {
            case .success(let weatherResponse):
                
                print("WeatherResponse: \(weatherResponse)")
                
                if let humidity = weatherResponse?.main?.humidity {
                    self?.humidityLabel.text = String(humidity)
                }
            case .failure(let error):
                
                print("Error: \(error.localizedDescription)")
            }
        }
    }
    
    @IBAction func showData(_ sender: Any) {

        retrieve()
    }

}



extension ViewController {

    func fetchWeather(forLocation location: CLLocation, completion: @escaping (Result<WeatherResponse?, Error>) -> Void) {
    
        let apikey = "YOUR_API_KEY_HERE"

        let url = URL(string: "https://api.openweathermap.org/data/2.5/weather?lat=\(location.coordinate.latitude)&lon=\(location.coordinate.longitude)&appid=\(apikey)")!
    
        var request = URLRequest(url: url)
        request.httpMethod = "GET"
    
        print("Request: \(url.absoluteString)")
        
        URLSession.shared.dataTask(with: request) { (data, response, error) in
        
            if let error = error {
                
                DispatchQueue.main.async { // Move completion to the main queue, so that you can work with UI stuff
                    completion(.failure(error))
                }
            }
        
            guard let data = data else {
                
                print("No response data.")
                
                DispatchQueue.main.async { // Move completion to the main queue, so that you can work with UI stuff
                    completion(.success(nil))
                }
                
                return
            }
            
            if let responseString = String(data: data, encoding: .utf8) { // Move completion to the main queue, so that you can work with UI stuff
                print("Response: \(responseString)")
            }
        
            do {
                let response = try JSONDecoder().decode(WeatherResponse.self, from: data) // Here is the magic of Codable, Just simply set expected Codable type
                
                DispatchQueue.main.async { // Move completion to the main queue, so that you can work with UI stuff
                    completion(.success(response))
                }
                
            } catch {
                
                DispatchQueue.main.async { // Move completion to the main queue, so that you can work with UI stuff
                    completion(.failure(error))
                }
            }
        }.resume()
    }
}

编码愉快,不要放弃)