使用 SwiftUI 和 Combine 不返回 OpenWeather 数据
OpenWeather data not returning using SwiftUI and Combine
我不确定为什么没有返回任何数据。我知道 API 键和 URL 键工作正常。下面是结构和我正在使用的 class。我还在我的 SwiftUI 文件中包含了我正在做的事情。
在响应中我可以看到我返回了 200。我尝试了几种不同的方法来将数据映射到我的视图,但到目前为止没有成功。
// MARK: - Welcome
struct WeatherDataModel: Codable, Identifiable {
let id = UUID()
let lat, lon: Double
let timezone: String
let current: Current
let daily: [Daily]
enum CodingKeys: String, CodingKey {
case lat, lon, timezone
case current, daily
}
}
// MARK: - Current
struct Current: Codable {
let dt, sunrise, sunset: Int
let temp, feelsLike: Double
let pressure, humidity: Int
let dewPoint: Double
let uvi, clouds, visibility: Int
let windSpeed: Double
let windDeg: Int
let weather: [Weather]
enum CodingKeys: String, CodingKey {
case dt, sunrise, sunset, temp
case feelsLike = "feels_like"
case pressure, humidity
case dewPoint = "dew_point"
case uvi, clouds, visibility
case windSpeed = "wind_speed"
case windDeg = "wind_deg"
case weather
}
}
// 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: - Daily
struct Daily: Codable {
let dt, sunrise, sunset, moonrise: Int
let moonset: Int
let moonPhase: Double
let temp: Temp
let feelsLike: FeelsLike
let pressure, humidity: Int
let dewPoint, windSpeed: Double
let windDeg: Int
let windGust: Double
let weather: [Weather]
let clouds: Int
let pop: Double
let rain: Double?
let uvi: Double
enum CodingKeys: String, CodingKey {
case dt, sunrise, sunset, moonrise, moonset
case moonPhase = "moon_phase"
case temp
case feelsLike = "feels_like"
case pressure, humidity
case dewPoint = "dew_point"
case windSpeed = "wind_speed"
case windDeg = "wind_deg"
case windGust = "wind_gust"
case weather, clouds, pop, rain, uvi
}
}
// MARK: - FeelsLike
struct FeelsLike: Codable {
let day, night, eve, morn: Double
}
// MARK: - Temp
struct Temp: Codable {
let day, min, max, night: Double
let eve, morn: Double
}
typealias weatherData = [WeatherDataModel]
class DownloadWeatherData: ObservableObject {
@Published var weatherdata: [WeatherDataModel] = []
var weatherCancellabes = Set<AnyCancellable>()
init() {
print("loading weather init")
getWeather(weatherUrl: "<my url>")
}
func getWeather(weatherUrl: String) {
guard let weatherUrl = URL(string: weatherUrl) else { return }
URLSession.shared.dataTaskPublisher(for: weatherUrl)
.subscribe(on: DispatchQueue.global(qos: .background))
.receive(on: DispatchQueue.main)
.tryMap { (data, response) -> Data in
print(response)
guard
let response = response as? HTTPURLResponse,
response.statusCode >= 200 && response.statusCode < 300 else {
throw URLError(.badServerResponse)
}
print("data \(data)")
return data
}
.decode(type: [WeatherDataModel].self, decoder: JSONDecoder())
.sink { (completion) in
} receiveValue: { [weak self] (returnedWeatherData) in
self?.weatherdata = returnedWeatherData
print("returnedWeatherData \(returnedWeatherData)")
}
.store(in: &weatherCancellabes)
}
}
struct WeatherView: View {
@StateObject var weatherData = DownloadWeatherData()
var body: some View {
VStack {
ForEach(weatherData.weatherdata) { day in
Text(day.timezone)
}
}
}
}
我得到的错误是No exact matches in call to initializer
提示:
- 您不需要定义 CodingKeys 枚举,只需为您的 JSON 解码器设置
.keyDecodingStrategy = .convertFromSnakeCase
;所有下划线键,如 wind_gust
,将自动转换为驼峰式,例如 windGust
。
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
使用可选而不是非可选模型的属性来确保任何丢失的 return 键不会使您的解码器失败。如果 属性 是后端开发人员 100% 的保证,您可以稍后删除可选的。
仔细检查来自服务器的响应字符串的数据类型,有时它们 return String
但我们使用模型 Double
或 Int
属性 并检查 Array
或 Dictionary
;根据我的经验,大多数时候根对象是 Dictionary
所以只需再次检查 [WeatherDataModel]
。
我不确定为什么没有返回任何数据。我知道 API 键和 URL 键工作正常。下面是结构和我正在使用的 class。我还在我的 SwiftUI 文件中包含了我正在做的事情。
在响应中我可以看到我返回了 200。我尝试了几种不同的方法来将数据映射到我的视图,但到目前为止没有成功。
// MARK: - Welcome
struct WeatherDataModel: Codable, Identifiable {
let id = UUID()
let lat, lon: Double
let timezone: String
let current: Current
let daily: [Daily]
enum CodingKeys: String, CodingKey {
case lat, lon, timezone
case current, daily
}
}
// MARK: - Current
struct Current: Codable {
let dt, sunrise, sunset: Int
let temp, feelsLike: Double
let pressure, humidity: Int
let dewPoint: Double
let uvi, clouds, visibility: Int
let windSpeed: Double
let windDeg: Int
let weather: [Weather]
enum CodingKeys: String, CodingKey {
case dt, sunrise, sunset, temp
case feelsLike = "feels_like"
case pressure, humidity
case dewPoint = "dew_point"
case uvi, clouds, visibility
case windSpeed = "wind_speed"
case windDeg = "wind_deg"
case weather
}
}
// 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: - Daily
struct Daily: Codable {
let dt, sunrise, sunset, moonrise: Int
let moonset: Int
let moonPhase: Double
let temp: Temp
let feelsLike: FeelsLike
let pressure, humidity: Int
let dewPoint, windSpeed: Double
let windDeg: Int
let windGust: Double
let weather: [Weather]
let clouds: Int
let pop: Double
let rain: Double?
let uvi: Double
enum CodingKeys: String, CodingKey {
case dt, sunrise, sunset, moonrise, moonset
case moonPhase = "moon_phase"
case temp
case feelsLike = "feels_like"
case pressure, humidity
case dewPoint = "dew_point"
case windSpeed = "wind_speed"
case windDeg = "wind_deg"
case windGust = "wind_gust"
case weather, clouds, pop, rain, uvi
}
}
// MARK: - FeelsLike
struct FeelsLike: Codable {
let day, night, eve, morn: Double
}
// MARK: - Temp
struct Temp: Codable {
let day, min, max, night: Double
let eve, morn: Double
}
typealias weatherData = [WeatherDataModel]
class DownloadWeatherData: ObservableObject {
@Published var weatherdata: [WeatherDataModel] = []
var weatherCancellabes = Set<AnyCancellable>()
init() {
print("loading weather init")
getWeather(weatherUrl: "<my url>")
}
func getWeather(weatherUrl: String) {
guard let weatherUrl = URL(string: weatherUrl) else { return }
URLSession.shared.dataTaskPublisher(for: weatherUrl)
.subscribe(on: DispatchQueue.global(qos: .background))
.receive(on: DispatchQueue.main)
.tryMap { (data, response) -> Data in
print(response)
guard
let response = response as? HTTPURLResponse,
response.statusCode >= 200 && response.statusCode < 300 else {
throw URLError(.badServerResponse)
}
print("data \(data)")
return data
}
.decode(type: [WeatherDataModel].self, decoder: JSONDecoder())
.sink { (completion) in
} receiveValue: { [weak self] (returnedWeatherData) in
self?.weatherdata = returnedWeatherData
print("returnedWeatherData \(returnedWeatherData)")
}
.store(in: &weatherCancellabes)
}
}
struct WeatherView: View {
@StateObject var weatherData = DownloadWeatherData()
var body: some View {
VStack {
ForEach(weatherData.weatherdata) { day in
Text(day.timezone)
}
}
}
}
我得到的错误是No exact matches in call to initializer
提示:
- 您不需要定义 CodingKeys 枚举,只需为您的 JSON 解码器设置
.keyDecodingStrategy = .convertFromSnakeCase
;所有下划线键,如wind_gust
,将自动转换为驼峰式,例如windGust
。
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
使用可选而不是非可选模型的属性来确保任何丢失的 return 键不会使您的解码器失败。如果 属性 是后端开发人员 100% 的保证,您可以稍后删除可选的。
仔细检查来自服务器的响应字符串的数据类型,有时它们 return
String
但我们使用模型Double
或Int
属性 并检查Array
或Dictionary
;根据我的经验,大多数时候根对象是Dictionary
所以只需再次检查[WeatherDataModel]
。