Here's probably the simplest way to fix this:
struct NetworkingView: View {
@State private var location: String = ""
@ScaledMetric var size: CGFloat = 100
//1
//make this a state property so we can change it
//make it Optional so we don't have to pass anything in at first
@State private var weatherModel: WeatherModel?
var body: some View {
ZStack {
Image("background")
.resizable()
.scaledToFill()
.ignoresSafeArea()
VStack {
HStack {
Button(action: {}) {
Image(systemName: "location.north.circle.fill")
.font(.largeTitle)
.padding(.horizontal)
}
TextField("Location", text: $location)
.textFieldStyle(.roundedBorder)
.multilineTextAlignment(.center)
.frame(width: 200)
Button(action: {Task{
await loadData()
}}) {
Image(systemName: "magnifyingglass")
.font(.largeTitle)
.padding(.horizontal)
}
}
.padding(.vertical)
//2
//we only want to show this stuff if we actually have
// a valid weatherModel
if let weather = weatherModel {
HStack {
Image(systemName: weather.conditionString)
.font(.system(size: size))
}
Text("\(weather.temperatureString) °C")
.font(.system(size: size))
.padding(.horizontal)
Text("Pressure: \(weather.pressure) mmHg")
.font(.headline)
.padding(.horizontal)
Text("Humidity: \(weather.humidity) %")
.font(.headline)
.padding(.horizontal)
Text(weather.cityName)
.font(.largeTitle)
.padding(.horizontal)
}
Spacer()
}
.accentColor(.primary)
}
}
func loadData() async {
guard let url = URL(string:"https://api.openweathermap.org/data/2.5/weather?q=\(location)&appid=133....764bae.............c8e9....908&units=metric") else {
print("Invalid URL")
return
}
do {
let (data, _) = try await URLSession.shared.data(from: url)
if let decodedResponse = try? JSONDecoder().decode(WeatherData_Network.self, from: data) {
let id = decodedResponse.weather[0].id
let temp = decodedResponse.main.temp
let cityName = decodedResponse.name
let pressure = decodedResponse.main.pressure
let humidity = decodedResponse.main.humidity
//3
//build a new WeatherModel from our JSON response
//and assign it to our state property
weatherModel = WeatherModel(conditionId: id, cityName: cityName, temperature: temp, pressure: pressure, humidity: humidity)
}
} catch {
print("Invalid Data")
}
}
}
- We make
weatherModel
an @State
property so it can be updated as the user searches for locations. We make it Optional so that we don't have to pass in a dummy value whenever our NetworkingView
struct is created.
- We only want to display weather results if we have retrieved a
weatherModel
from the API call.
- You were on the right track here, but you need to take the new
WeatherModel
instance and assign it to the weatherModel
property so the UI will update.
There are other ways of handling this, using view models or service classes, but this way is simple and may meet your needs. Try it, play around with it, do some research, etc.
I'll also note that you have an issue with locations that contain a space or non-ASCII character, like "Los Angeles" or "São Paolo". You need to properly encode the location string before passing it to the API call to handle those spaces and other characters.