skip to Main Content

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 :(")
        }
    } ```

2

Answers


  1. 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 ?? "")")
    
    Login or Signup to reply.
  2. 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 ?? []) { weather in
                            Section(header:Text("Address")) {
                                Text("Current conditions: (weather.main)")
                                    .font(.headline)
                                    .bold()
                                Text("Weather description: (weather.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)")
            }
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search