swiftuiswiftui-state

@State var doesn't store value


My goal is to pass values between views, from Chooser to ThemeEditor. When the user presses an icon, I'm saving the object that I want to pass and later, using sheet and passing the newly created view with the content of the @State var.

The assignment is done successfully to the @State var themeToEdit, however it is nil when the ThemeEditor view is created in the sheet

What am I doing wrong?

struct Chooser: View {
    @EnvironmentObject var store: Store
    
    @State private var showThemeEditor = false
    @State private var themeToEdit: ThemeContent?
    @State private var editMode: EditMode = .inactive
    @State private var isValid = false
    
    var body: some View {
        
        NavigationView {
            List {
                ForEach(self.store.games) { game in
                    NavigationLink(destination: gameView(game))
                    {
                        Image(systemName: "wrench.fill")
                            .imageScale(.large)
                            .opacity(editMode.isEditing ? 1 : 0)
                            .onTapGesture {
                                self.showThemeEditor = true
                                /* themeInfo is of type struct ThemeContent: Codable, Hashable, Identifiable */
                                self.themeToEdit = game.themeInfo 
                            }
                        VStack (alignment: .leading) {
                            Text(self.store.name(for: something))
                                
                            HStack{
                                /* some stuff */
                                Text(" of: ")
                                Text("Interesting info")
                            }
                        }
                    }
                }
                .sheet(isPresented: $showThemeEditor) {
                    if self.themeToEdit != nil { /* << themeToEdit is nil here - always */
                        ThemeEditor(forTheme: self.themeToEdit!, $isValid)
                    } 
                }
            }
            .environment(\.editMode, $editMode)
        }
    }
}


struct ThemeEditor: View {
    @State private var newTheme: ThemeContent
    @Binding var isValid: Bool
    @State private var themeName = ""
    
    init(forTheme theme: ThemeContent, isValid: Binding<Bool>) {
        self._newTheme = State(wrappedValue: theme)
        self._validThemeEdited = isValid
    }
    var body: some View {
           ....
    }
}

struct ThemeContent: Codable, Hashable, Identifiable {
/* stores simple typed variables of information */
}

Solution

  • The .sheet content view is captured at the moment of creation, so if you want to check something inside, you need to use .sheet(item:) variant, like

    .sheet(item: self.$themeToEdit) { item in
        if item != nil {
            ThemeEditor(forTheme: item!, $isValid)
        } 
    }
    

    Note: it is not clear what is ThemeContent, but it might be needed to conform it to additional protocols.