Home > database >  "Out of index" error within SwiftUI ContentView but not in function
"Out of index" error within SwiftUI ContentView but not in function

Time:05-20

I have a weather app that I am creating. I use an API (OpenWeatherApp) to get my data. I load the data into an array, weatherArray, of type struct WeatherJSON.

I can successfully access the data using a ForEach loop, but when I try to directly access specific data using array[index], I get the following error: Thread 1: Fatal error: Index out of range

Here is the top-level struct that I am using:

// holds all data from the JSON request
struct WeatherJSON: Codable {
    var coord: Coord            // coordinate struct
    var weather: [Weather]      // array of Weather struct
    var base: String            // "internal parameter..?"
    var main: Main              // main struct (contains the juicy data)
    var visibility: Int         // visibility number
    var wind: Wind              // wind struct
    var clouds: Clouds          // clouds struct
    var dt: Int                 // time of data calculation, unix, UTC
    var sys: Sys                // internal parameer
    var timezone, id: Int       // timezone
    var name: String            // city namme
    var cod: Int                // another internal parameter (..?)
} 

My ContentView:

struct ContentView: View {
    
    @State private var weatherArray: [WeatherJSON] = []
//    @State private var weatherArray: [Weather] = []
    
    var body: some View {
        NavigationView {
            VStack {
                Text("\(weatherArray[0].name)") // <---- This doesn't work
                List {
//                    Text("Current conditions: \(weatherArray[0].description)")
                    
//                    ForEach(weatherArray, id: \.self) { result in
//                        Section(header:Text("Address")) {
//                            Text("Current conditions: \(result.main)")
//                                .font(.headline)
//                                .bold()
//                            Text("Weather description: \(result.description)")
//                                .font(.body)
//                        }
//                    }
                }
                .navigationTitle("Weather")
            }
        }
        .task { await handleData() }
    }
    
    func handleData() async {
        
        guard let url = URL(string: "https://api.openweathermap.org/data/2.5/weather?q=Seattle&appid={APIKEY}") else {
            print("This URL does not work!")
            return
        }
        
        let decoder = JSONDecoder()
        
        do {
            let (weatherData, _) = try await URLSession.shared.data(from: url)
            if let weatherObj = try? decoder.decode(WeatherJSON.self, from: weatherData) {
                weatherArray.append(weatherObj)// = weatherObj.weather
                print(weatherArray[0]) // <--- This works?
            }

        } catch {
            print("Did not work :(")
        }
    } ```

CodePudding user response:

Your view is accessing the weatherArray before the data has been loaded. You need to account for a possibly empty array to avoid the crash.

Change:

Text("\(weatherArray[0].name)") // <---- This doesn't work

To:

Text("\(weatherArray.first?.name ?? "")")

CodePudding user response:

try this example code to get the weather data into your models and use it in your view:

public struct Weather: Identifiable, Codable {
    public let id: Int
    public let main, description, icon: String
}

struct Coord: Codable {
    var lon: Double
    var lat: Double
}

// holds all data from the JSON request
struct WeatherJSON: Codable {
    var coord: Coord             // coordinate struct
    var weather: [Weather]       // array of Weather struct
    var base: String             // "internal parameter..?"
    var main: Main              // main struct (contains the juicy data)
    var visibility: Int         // visibility number
    var wind: Wind              // wind struct
    var clouds: Clouds          // clouds struct
    var dt: Int                  // time of data calculation, unix, UTC
    var sys: Sys                // internal parameer
    var timezone, id: Int        // timezone
    var name: String             // city namme
    var cod: Int                 // another internal parameter (..?)
}


struct ContentView: View {
    
    @State private var city: WeatherJSON?
    
    var body: some View {
        NavigationView {
            VStack {
                Text("\(city?.name ?? "no name")")
                List {
                    ForEach(city?.weather ?? []) { result in
                        Section(header:Text("Address")) {
                            Text("Current conditions: \(result.main)")
                                .font(.headline)
                                .bold()
                            Text("Weather description: \(result.description)")
                                .font(.body)
                        }
                    }
                }
                .navigationTitle("Weather")
            }
        }
        .task { await handleData() }
    }
    
    func handleData() async {
        guard let url = URL(string: "https://api.openweathermap.org/data/2.5/weather?q=Seattle&appid={apikey}") else {
            print("This URL does not work!")
            return
        }
        do {
            let (weatherData, _) = try await URLSession.shared.data(from: url)
            city = try JSONDecoder().decode(WeatherJSON.self, from: weatherData)
            print(city)
        } catch {
            print("error: \(error)")
        }
    }
}
  • Related