我正在嘗試構建一個基本的 SwiftUI 天氣應用程式。該應用程式允許用戶使用 OpenWeatherMap API 按城市名稱搜索天氣。我將文本欄位中輸入的城市名稱配置為注入到 name: "" 在 WeatherModel 中,在 viewModel 中的 fetchWeather() 函式內。然后我配置了 OpenWeatherMap URL 字串以將 searchedCity.name 作為引數(參見下面的 viewModel)。此設定似乎作業正常,因為我可以按城市名稱搜索天氣。但是,我想就將 searchCity.name 直接傳遞到 URL(在 viewModel 中)的做法是否正確尋求反饋。關于:
let searchedCity = WeatherModel(...
...我不確定如何處理 WeatherModel 實體中的 CurrentWeather 和 WeatherInfo。由于我只使用“searchedCity”將城市名稱傳遞到 URL 中,“CurrentWeather.init(temp: 123.00)”和“weather: [WeatherInfo.init(description: "")]” 應該如何設定? 實作 temp 和 description 的值(例如 123 和“”)是否正確?
下面是我的完整代碼:
內容視圖
import SwiftUI
struct ContentView: View {
// Whenever something in the viewmodel changes, the content view will know to update the UI related elements
@StateObject var viewModel = WeatherViewModel()
// @State private var textField = ""
var body: some View {
NavigationView {
VStack {
TextField("Enter City Name", text: $viewModel.enterCityName).textFieldStyle(.roundedBorder)
Button(action: {
viewModel.fetchWeather()
viewModel.enterCityName = ""
}, label: {
Text("Search")
.padding(10)
.background(Color.green)
.foregroundColor(Color.white)
.cornerRadius(10)
})
Text(viewModel.title)
.font(.system(size: 32))
Text(viewModel.temp)
.font(.system(size: 44))
Text(viewModel.descriptionText)
.font(.system(size: 24))
Spacer()
}
.navigationTitle("Weather MVVM")
}.padding()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
模型
import Foundation
// Data, Model should mirror the JSON layout
//Codable is the property needed to convert JSON into a struct
struct WeatherModel: Codable {
let name: String
let main: CurrentWeather
let weather: [WeatherInfo]
}
struct CurrentWeather: Codable {
let temp: Float
}
struct WeatherInfo: Codable {
let description: String
}
視圖模型
import Foundation
class WeatherViewModel: ObservableObject {
//everytime these properties are updated, any view holding onto an instance of this viewModel will go ahead and updated the respective UI
@Published var title: String = "-"
@Published var temp: String = "-"
@Published var descriptionText: String = "-"
@Published var enterCityName: String = ""
init() {
fetchWeather()
}
func fetchWeather() {
let searchedCity = WeatherModel(name: enterCityName, main: CurrentWeather.init(temp: 123.00), weather: [WeatherInfo.init(description: "")])
guard let url = URL(string: "https://api.openweathermap.org/data/2.5/weather?q=\(searchedCity.name)&units=imperial&appid=d2c9aaafb6c5d4d2632592ce88154c5f") else {
return
}
let task = URLSession.shared.dataTask(with: url) { data, _, error in
// get data
guard let data = data, error == nil else {
return
}
//convert data to model
do {
let model = try JSONDecoder().decode(WeatherModel.self, from: data)
DispatchQueue.main.async {
self.title = model.name
self.temp = "\(model.main.temp)"
self.descriptionText = model.weather.first?.description ?? "No Description"
}
}
catch {
print(error)
}
}
task.resume()
}
}
uj5u.com熱心網友回復:
我想就將 searchCity.name 直接傳遞到 URL(在 viewModel 中)的做法是否正確尋求反饋。
您應該始終避免將假值傳遞給像 Int(123) 這樣的物件/類。相反,您應該使用可為空的結構或類。
我認為不需要創建一個完整的 WeatherModel 實體只是為了從中讀取一個屬性,一個你已經在 viewmodel 的enterCityName屬性中的屬性。只需使用視圖模型的enterCityName 屬性即可。
uj5u.com熱心網友回復:
有很多方法可以按照您的要求進行操作,以下代碼只是一種方法。由于您只需要城市名稱即可獲得結果,因此只需在 url 字串中使用該名稱即可。還使用您的WeatherModelinWeatherViewModel避免將資料復制到各種中間變數中。
PS:不要在您的網址中發布您的秘密 appid 密鑰。
import Foundation
import SwiftUI
@main
struct TestApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
struct ContentView: View {
@StateObject var viewModel = WeatherViewModel()
@State private var cityName = "" // <-- use this to get the city name
var body: some View {
NavigationView {
VStack {
TextField("Enter City Name", text: $cityName).textFieldStyle(.roundedBorder)
Button(action: {
viewModel.fetchWeather(for: cityName) // <-- let the model fetch the results
cityName = ""
}, label: {
Text("Search")
.padding(10)
.background(Color.green)
.foregroundColor(Color.white)
.cornerRadius(10)
})
// --- display the results ---
Text(viewModel.cityWeather.name).font(.system(size: 32))
Text("\(viewModel.cityWeather.main.temp)").font(.system(size: 44))
Text(viewModel.cityWeather.firstWeatherInfo()).font(.system(size: 24))
Spacer()
}
.navigationTitle("Weather MVVM")
}.navigationViewStyle(.stack)
}
}
class WeatherViewModel: ObservableObject {
// use your WeatherModel that you get from the fetch results
@Published var cityWeather: WeatherModel = WeatherModel()
func fetchWeather(for cityName: String) {
guard let url = URL(string: "https://api.openweathermap.org/data/2.5/weather?q=\(cityName)&units=imperial&appid=YOURKEY") else { return }
let task = URLSession.shared.dataTask(with: url) { data, _, error in
guard let data = data, error == nil else { return }
do {
let model = try JSONDecoder().decode(WeatherModel.self, from: data)
DispatchQueue.main.async {
self.cityWeather = model
}
}
catch {
print(error) // <-- need to deal with errors here
}
}
task.resume()
}
}
struct WeatherModel: Codable {
var name: String = ""
var main: CurrentWeather = CurrentWeather()
var weather: [WeatherInfo] = []
func firstWeatherInfo() -> String {
return weather.count > 0 ? weather[0].description : ""
}
}
struct CurrentWeather: Codable {
var temp: Float = 0.0
}
struct WeatherInfo: Codable {
var description: String = ""
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/355018.html
下一篇:隨機選擇歌曲的方式/Swift
