-1

I am trying to update a state variable that is an array of Strings that I get back from an API call. I want to display this array in a Picker. However, my Picker is completely blank. I know my api is coming back with values because one of the ForEach's is updating.

struct SelectModeView: View {
    @StateObject var viewModel = SelectModeViewModel()
    @State private var selectionB: String = ""
    
    var body: some View {
           ZStack {
               Color.white
                   .edgesIgnoringSafeArea(.all)
               VStack {
                     
                       VStack(spacing: 08) {
                           Text("PICKER STYLE")
                                 .font(.largeTitle)
                               Text("Select Project")
                                   
                           Picker("Select Project", selection: self.$selectionB) {
                               ForEach(self.projects, id: \.self) { option in
                                   Text(option)
                               }
                           }
                           .accentColor(Color.black)
                       }
                       
                       Text("DISCLOSURE GROUP STYLE")
                             .font(.largeTitle)
                       DisclosureGroup(self.selectionC == "" ? "Pick Project" : self.selectionB, isExpanded: self.$isProjectOpen, content: {
                           ScrollView(.vertical, showsIndicators: false) {
                               VStack {
                                   ForEach(self.projects, id: \.self) { option in
                                       Text(option)
                                           .padding()
                                   }
                               }
                           }
                           .frame(height: 240)
                       })
                       .accentColor(Color.black)
                       .padding(.horizontal, 64)
               }
           }
           .onAppear {
               print("selectModeView appeared")

               self.viewModel.gatherProjectListAPI()
           }
           .onReceive(self.viewModel.$projectList, perform: { list in
               print("Received project list: \(list)")
               self.projects = list
           })
           
       }
}
    

class SelectModeViewModel: ObservableObject {
    @Published var projectList: [String] = []
    func gatherProjectListAPI() {
     self.projectList.append("Project1")
     self.projectList.append("Project2")
     self.projectList.append("Project3")
         self.projectList.append("Project4")

//  same result as self.projectList = ["Project1", "Project2", "Project3", "Project4"]
     }
    
}

I have tried not using an id, going by only count of the array (0..<projects.count), not using " option in" and using $0 instead, using only the published variable in the viewModel (which would be preferred to do) and not only having the published variable being received and updating a state variable and have still not been able to get the Picker to update.

1
  • Have you tried using viewModel.projectList in the ForEach, instead of your nonsensical self.projects. Note, there is no need for your .onReceive(...), the projectList is already being observed by the view. Note also, using ForEach(viewModel.projectList, id: \.self) may work but it is not appropriate, you should make sure the elements are unique by having a unique id. Use the id for the selection and for Text(option).tag(id). With the current code, you should add selectionB = viewModel.projectList.first ?? "" to your .onAppear. Commented Jul 21 at 22:49

1 Answer 1

0

Try this approach using viewModel.projectList and a dedicated structure (struct Project) for your projectList. There is no need for your .onReceive(...), the projectList is already being observed by the view.

Note the Text(option.name).tag(option) and the ForEach(...). Initialise the selections in the .onAppear as shown.

You should not use ForEach(self.projects, id: \.self).


struct ContentView: View {
    var body: some View {
        SelectModeView()
    }
}

struct Project: Identifiable, Hashable {  // <--- here
    let id = UUID()
    var name: String
}

class SelectModeViewModel: ObservableObject {
    @Published var projectList: [Project] = []  // <--- here
    
    func gatherProjectListAPI() {
        self.projectList.append(Project(name: "Project1"))  // <--- here
        self.projectList.append(Project(name: "Project2"))
        self.projectList.append(Project(name: "Project3"))
        self.projectList.append(Project(name: "Project4"))
    }
}

struct SelectModeView: View {
    @StateObject private var viewModel = SelectModeViewModel()
    @State private var selectionB = Project(name: "") // <--- here
    @State private var selectionC = Project(name: "") // <--- here
    @State private var isProjectOpen = true
    
    
    var body: some View {
        ZStack {
            Color.white
                .edgesIgnoringSafeArea(.all)
            VStack {
                
                VStack(spacing: 08) {
                    Text("PICKER STYLE")
                        .font(.largeTitle)
                    Text("Select Project")
                    
                    Picker("Select Project", selection: self.$selectionB) {
                        ForEach(viewModel.projectList) { option in  // <--- here
                            Text(option.name)
                                .tag(option) // <--- here
                        }
                    }
                    .accentColor(Color.black)
                }
                
                Text("DISCLOSURE GROUP STYLE")
                    .font(.largeTitle)
                DisclosureGroup(self.selectionC.name == "" ? "Pick Project" : self.selectionB.name, isExpanded: self.$isProjectOpen, content: {
                    ScrollView(.vertical, showsIndicators: false) {
                        VStack {
                            ForEach(viewModel.projectList) { option in  // <--- here
                                Text(option.name)
                                    .tag(option) // <--- here
                                    .padding()
                            }
                        }
                    }
                    .frame(height: 240)
                })
                .accentColor(Color.black)
                .padding(.horizontal, 64)
            }
        }
        .onAppear {
            print("selectModeView appeared")
            viewModel.gatherProjectListAPI()
            selectionB = viewModel.projectList.first ?? Project(name: "")  // <--- here
            selectionC = selectionB // <--- here
        }
    }
}

Sign up to request clarification or add additional context in comments.

1 Comment

did my answer not worked for you?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.