Home > Software design >  How to make 'Published<[String]>.Publisher' conform to 'RandomAccessCollection&
How to make 'Published<[String]>.Publisher' conform to 'RandomAccessCollection&

Time:10-31

I am trying to make a view that updates based on if the user toggles the favorite button or not. I want the entire view to reconstruct in order to display an array of values whenever that array of values is changed. Inside the view, a for each loop should display every value in the array.

The view that I want to update every time savedArray is changed is FavView. But when I try to use a foreach loop to display every value is savedArray(which I created as a @Published so the view would reconstruct), it gives me the error Generic struct 'ForEach' requires that 'Published<[String]>.Publisher' conform to 'RandomAccessCollection'. I am confused because I thought that String arrays were able to be used in for each loops. Is this not true? How do I loop through a @Published array? Thank you! This is my code for the savedArray(in ViewModel) and the FavView I want to display it in with the for each.

final class ViewModel: ObservableObject {
        @Published var items = [Item]()
        @Published var showingFavs = true
        @Published var savedItems: Set<String> = []
        @Published var savedArray: [String]
        // Filter saved items
    
        var filteredItems: [String]  {
            //return self.items
            return savedArray
        }

        var db = Database()
        
        init() {
            self.savedItems = db.load()
            self.items = db.returnList()//the items
            self.savedArray = Array(db.load())
            print("savedarray", savedArray)
            print("important!", self.savedItems, self.items)
        }
        
        func contains(_ item: Item) -> Bool {
                savedItems.contains(item.id)
            }
        
        // Toggle saved items
        func toggleFav(item: Item) {
            print("Toggled!", item)
            if contains(item) {
                savedItems.remove(item.id)
                if let index = savedArray.firstIndex(of: item.id) {
                    savedArray.remove(at: index)
                }
            } else {
                savedItems.insert(item.id)
                savedArray.append(item.id)
            }
            db.save(items: savedItems)
        }
    }

struct FavView: View {
    @StateObject private var vm = ViewModel()
    
    var body: some View {
        VStack {
            
            List {
  
                ForEach($vm.savedArray, id: \.self) { string in
                    let item = vm.db.returnItem(input: string)
                    HStack {
                        VStack(alignment: .leading) {
                            Text(item.title)
                                .font(.headline)
                            
                            Text(item.description)
                                .font(.subheadline)
                        }
                        Spacer()
                        Image(systemName: vm.contains(item) ? "bookmark.fill" : "bookmark")
                            .foregroundColor(.blue)
                            .onTapGesture {
                                vm.toggleFav(item: item)
                            }
                    }
                }//my error is here at the for each
            }
            .cornerRadius(10)
        }
    }
}

//the returnItem function(it's in another file) and accepts a String
    func returnItem(input: String) -> Item{
            let vm = ViewModel()
            let test = cityClass()
            let temp = test.getConvert(input: input)//a datafrime slice
            let state: String = (temp["state", String.self].first ?? "There was an error") ?? "There was an error"
            let zip = ((temp["zip", String.self].first ) ?? "There was an error" ) ?? "There was an error"
            
            let id = input
            let title: String = (temp["primary_city", String.self].first ?? "There was an error") ?? "There was an error"
            let description = "\(state) \(zip)"

        if(vm.contains(Item(id: id, title: title, description: description, isFaved: true)) == false){
            temptList.append(Item(id: id, title: title, description: description, isFaved: true))
        }
        
        print("TEMPLIST", temptList)
            return Item(id: id, title: title, description: description, isFaved: true)
        }

CodePudding user response:

in ForEach, you are using $ symbol to access savedArray you have to use the vm itself

struct FavView: View {
    @StateObject private var vm = ViewModel()
    
    var body: some View {
        VStack {
            
            List {
  
                ForEach($vm.savedArray, id: \.self) { string in //< here $vm.savedArray not vm.$savedArray
                    let item = vm.db.returnItem(input: string)
                    HStack {
                        VStack(alignment: .leading) {
                            Text(item.title)
                                .font(.headline)
                            
                            Text(item.description)
                                .font(.subheadline)
                        }
                        Spacer()
                        Image(systemName: vm.contains(item) ? "bookmark.fill" : "bookmark")
                            .foregroundColor(.blue)
                            .onTapGesture {
                                vm.toggleFav(item: item)
                            }
                    }
                }
            }
            .cornerRadius(10)
        }
    }
}

this should work.

  • Related