swiftswiftuiobservedobject

SwiftUI does not update view ( add item to array ) until i restart canvas(simulator)


The items that i added from addHabitView does not update the view (i call them with for loop)

struct Habit : Identifiable, Codable, Equatable{
    var id = UUID()
    let habitName : String
    let importancyRatio : Int
    
}
class Habits : ObservableObject {
    @Published var items = [Habit](){
        didSet {
            let encoder = JSONEncoder()
            if let encoded = try? encoder.encode(items){
                UserDefaults.standard.set(encoded, forKey: "Habits")
            }
        }

    }
    init(){
        if let savedItems = UserDefaults.standard.data(forKey: "Habits"){
            if let decodedItems = try? JSONDecoder().decode([Habit].self, from: savedItems){
                items = decodedItems
                return
            }
        }else { items = []}
    }
}
struct addHabitView: View {
    @StateObject var habits : Habits
    @Environment(\.dismiss) var dismiss  
    @State private var habitName = ""
    @State private var importancyRatio = 0
    @State private var importancyRatios = [0,1,2,3,4]   
    var body: some View {
        NavigationView{
            Form{
                TextField("Habit:",text: $habitName)
                Picker("Importancy" ,selection: $importancyRatio){
                    ForEach(importancyRatios, id:\.self){
                        Text("\($0)")
                    }
                }
                .pickerStyle(.segmented)
            }
            .navigationTitle("Add Habit:")
            .navigationViewStyle(StackNavigationViewStyle())
            .toolbar{
                Button("Save"){
                    let habit = Habit(habitName: habitName, importancyRatio: importancyRatio)
                    if (habit.habitName.isEmpty){
                        dismiss()
                    }else{
                        habits.items.append(habit)
                    }
                }
            }
        }
    }
}
struct ContentView: View {
    @StateObject var habits = Habits()
    @State private var addSheet = false
    
    let columns = [
        GridItem(.adaptive(minimum: 200))
    ]
    
    var body: some View {
        NavigationView{
            VStack{
                ScrollView{
                    LazyVGrid(columns: columns){
                        VStack{
                            ForEach(habits.items, id: \.id) {habit in
                                NavigationLink{
                                    Text("Hey")
                                } label: {
                                    ZStack{
                                        habitrectangled(with: habit.habitName)
                                        Text("\(habit.importancyRatio)")
                                            .offset(CGSize(width: 150, height: 0))
                                            .foregroundColor(.white)
                                            .fontWeight(.heavy)
                                    }
                                }
                            }
                            .onDelete(perform: removeRows)
                        }
                    }
                }
                Button{
                    addSheet = true
                }label:{
                    Image(systemName: "plus")
                }
            }
            .sheet(isPresented: $addSheet){
                addHabitView(habits: Habits())
            }
            .navigationTitle("HabitTracker_v2")
            .navigationBarItems(trailing: EditButton())
        }
    }
}

When i add a item from addSheet view it does not come to the list immediately i need to restart simulator to see added items. I thought there could be a problem about init() but couldn't find the solution. I tried self.objectWillChange.send() but didn't work for me. I also tried changing observedobject with enviromentobject and nothing changed. I coulnd't even find where am i missing can this be a bug?


Solution

  • In your .sheet you are creating a new instance of Habits. Instead, use the one you already created via @StateObject.

    So, instead of

    .sheet(isPresented: $addSheet){
                addHabitView(habits: Habits())
            }
    

    Change it to

    .sheet(isPresented: $addSheet){
                addHabitView(habits: habits)
            }
    

    Also in your addHabitView, change habits to be an @ObservedObject instead of a @StateObject.

    This

    @StateObject var habits : Habits
    

    Should be this instead

    @ObservedObject var habits : Habits