为什么我无法从开放天气 api 接收到任何数据?
Why am I not able to recieve any data from the open weather api?
我正在使用 swift,我想接收温度和湿度的数据,但我收到的是零。我在另一个 swift 文件中有两个对象 temp
和 humdity
。我在我的代码中做错了什么?不确定我错过了什么。
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()
}
}
嗯,据我所知,您的代码中几乎没有错误。
- 您将永远不会收到位置更新,因为您没有设置 CLLocationManagerDelegate,您没有请求使用位置的授权,您还没有要求位置管理器开始更新位置。
- 您的响应解析代码似乎不正确。您需要学习如何使用 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()
}
}
编码愉快,不要放弃)
我正在使用 swift,我想接收温度和湿度的数据,但我收到的是零。我在另一个 swift 文件中有两个对象 temp
和 humdity
。我在我的代码中做错了什么?不确定我错过了什么。
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()
}
}
嗯,据我所知,您的代码中几乎没有错误。
- 您将永远不会收到位置更新,因为您没有设置 CLLocationManagerDelegate,您没有请求使用位置的授权,您还没有要求位置管理器开始更新位置。
- 您的响应解析代码似乎不正确。您需要学习如何使用 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()
}
}
编码愉快,不要放弃)