0

I am trying to update a List using an availableModels array inside an EnvironmentObject. For some reason it doesn't refresh my view but I have no idea why. Very confused about this so help would be much appreciated, I'm new to SwiftUI and transferring data between views. I'm pretty sure that the array is updating because when I print it out the values are correct, but the list doesn't update in the ForEach loop or in the Text fields.

This is my view with the List:

import SwiftUI

var selectedModel: String = "rdps"

struct ModelSelectView: View {
    @EnvironmentObject var viewModel: WeatherData
    
    
    var body: some View {

        NavigationView {
            List{
                Group {
                    WeatherModelHeader()
                }
                if !viewModel.weatherArray.isEmpty {
                    ForEach(viewModel.weatherArray[0].availableModels, id: \.apiName) { model in
                        WeatherModelCell(weatherModel: model)
                    }
                } else {
                    Text("No Weather 4 u")
                }
                Button(action: {
                    fetchModelInventory(for: viewModel)
                }, label: {
                    Text("Fetch Inventory")
                })
                Text(String(viewModel.weatherArray[0].availableModels[0].apiName))
                Text(String(viewModel.weatherArray[0].availableModels[1].apiName))
                Text(String(viewModel.weatherArray[0].availableModels[2].apiName))
                Text(String(viewModel.weatherArray[0].availableModels[3].apiName))
                

                
            }
            .navigationTitle("Models")
            .onAppear(perform: {
                fetchModelInventory(for: viewModel)

                print("viewModel.weatherArray.availableModels \(viewModel.weatherArray[0].availableModels)")
                
            })
            
        }
    }
}


//Layout for the header of the list
struct WeatherModelHeader: View {
    var body: some View {
        HStack {
            Text("Model \nName")
                .bold()
                .frame(width: 60, alignment: .leading)
            Text("Range")
                .bold()
                .frame(width: 80, alignment: .leading)
                .padding(.leading, 30)
            
            Text("Resolution")
                .bold()
                .frame(width: 85, alignment: .leading)
                .padding(.leading, 30)
        }
    }
}

//create the layout for the weather model list
struct WeatherModelCell: View {
    let weatherModel: WeatherModel
    @EnvironmentObject var viewModel: WeatherData
    
    var body: some View {
        
        HStack {
            //need to make navlink go to correct graph model. This will be passed in the GraphViews(). Clicking this nav link will trigger the API Call for the coordinates and model.
            NavigationLink(
                destination: InteractiveChart()
                    .onAppear{
                        selectedModel = weatherModel.apiName
                        fetchData(for: viewModel)
                    },
                label: {

                Text(weatherModel.name)
                    .frame(width: 60, alignment: .leading)
                Text("\(String(weatherModel.range)) days")
                    .frame(width: 80, alignment: .leading)
                    .padding(.leading, 30)
                
                Text("\(String(weatherModel.resolution)) km")
                    .frame(width: 80, alignment: .leading)
                    .padding(.leading, 30)
              //this triggers the api call when the model is tapped. it passes the selected model name to the variable selectedModel for the call.
            })
            
        }
    }
}

And these are my models where I set up my Observable Object:

class WeatherForecastClass: ObservableObject {
    //var id = UUID()
    var chartMODEL: String = "Model"
    var chartModelHeight: Int = 0
    var chartLAT: Double = selectedCords.latitude
    var chartLON: Double = selectedCords.longitude
    var chartDATETIME: Date = formatDate(date: "2023-02-10 18:00")
    var chartTMP: Int = 1
    var chartRH: Int = 0
    var chartDP: Float = 0.0
    var chartTTLSnow: Float = 0.0
    var chartTTLRain: Float = 0.0
    var chartWindSpeed: Int = 0
    var chartWindDirDegs: Int = 0
    var chartWindDirString: String = ""
    var availableModels: [WeatherModel] = weatherModels
   
}

class WeatherData: ObservableObject {
    ///  weather data. This is the master class.
    @Published var weatherArray: [WeatherForecastClass] = [WeatherForecastClass()]
    //@Published var weatherProperties: WeatherForecastClass = WeatherForecastClass()
}

And this is my function for updating the availableModels array:

import Foundation
import SwiftUI

var modelInventory: [String] = []

func fetchModelInventory(for weatherData: WeatherData) {
//    @EnvironmentObject var viewModel: WeatherData
    
    let lat = Float(selectedCords.latitude)
    let lon = Float(selectedCords.longitude)
    let key = "my apiKey"
   
    //this is the url with the API call. it has the data for the call request and my API key.
    let urlString = "https://spotwx.io/api.php?key=\(key)&lat=\(lat)&lon=\(lon)&model=inventory"
    guard let url = URL(string: urlString) else {
        return
    }
    print(url)
    //Gets the data from the api call. tasks are async
    let task = URLSession.shared.dataTask(with: url) { data, _, error in
        guard let data = data, error == nil else {
            print("Error")
            return
        }
        //clear the model inventory
        modelInventory = []
        weatherData.weatherArray[0].availableModels = []
        
        //decode the modelinventory csv and fill the array
        if let csvString = String(data: data, encoding: .utf8) {
            let lines = csvString.split(separator: "\n")
            for line in lines {
                let columns = line.split(separator: ",")
                for column in columns {
                    let value = String(column)
                    modelInventory.append(value)
                    
                }
            }
        }
        print("Model Inventory -----")
        print(modelInventory)
        if !modelInventory.isEmpty {
            DispatchQueue.main.async {

                for model in weatherModels {
                    if modelInventory.contains(model.apiName) {
                        weatherData.weatherArray[0].availableModels.append(model)
                    }
                }
                print(weatherData.weatherArray[0].availableModels)
            }
        } else {
            return
        }
    }
    task.resume()
}

I feel like I've tried everything but I no matter what the list wont update.

SkimoBen
  • 19
  • 3
  • You have to add “@Published” to the variables in the ObservableObject – lorem ipsum Feb 20 '23 at 01:35
  • 1
    You have an array of `class WeatherForecastClass: ObservableObject` in your `class WeatherData: ObservableObject` **Nesting** ObservableObject classes like you do, is not a good idea and often result in problems. You could try making `WeatherForecastClass` a struct. – workingdog support Ukraine Feb 20 '23 at 02:20
  • @workingdogsupportUkraine I think you were right. After changing a bunch of stuff it works so hard to pinpoint it, but one thing I did was take availableModels out of the WeatherForecastClass and made it its own "@Published" var in the WeatherData class. – SkimoBen Feb 20 '23 at 07:14

0 Answers0