Update View when viewModel is modified

Hello everyone, I need to update view with core data values but I'm doing something wrong with @StateObject or @Observedobject or something like this.

Explanation

I have 1 main view called MainView that contains 2 views

TagViewPhone() this view contains a list of buttons, the idea is when you click a button it filters the list of words inside WordView2()

For example if you click Tag ( Colors) Only show the words that have the relation with Tag (Colors)

Problem

In my WordViewModel.getWordsFor(tag:Tag) filters that, and it works but WordView2() never update the view

Core data entities

I have 2 Entities with relation Many to Many

Word : id,text and relation wordToTag with destination Tag and inverse tagToWord

Tag: id,text, and relation tagToWord with destination Word and inverse wordToTag

Views

MainView

struct MainView2: View {
    var body: some View {
        ZStack{
            Color("colorBackground").edgesIgnoringSafeArea(.all)
            VStack{
                TagViewPhone()
                WordView2().padding(.horizontal)
            }
        }
    }
}

WordView2

struct WordView2: View {    
    @ObservedObject private var wordViewModel = WordViewModel()    
    var body: some View {
        ScrollView(.vertical){
            ForEach(wordViewModel.words) { word in
                    Text(word.text ?? "X")
            }
        }
    }
}

TagViewPhone

struct TagViewPhone: View {
    @Environment(\.managedObjectContext) private var viewContext
    @FetchRequest(sortDescriptors: [NSSortDescriptor(keyPath: \Tag.text, ascending: true)],animation: .default)
    
    private var tags:FetchedResults<Tag>

    @ObservedObject var wordViewModel = WordViewModel()
    
    var body: some View {
        ScrollView(.horizontal) {
            HStack{
                ForEach(tags, id:\.id) {tag in
                    Button {
                        wordViewModel.getWordsFor(tag: tag)
                    } label: {
                        HStack{
                            Text(tag.text!).bold()
                                .padding(.horizontal)
                                .padding(.vertical,5)
                        }
                    }
                }
            }.padding(.all)
        }

    }
}

ViewModel

WordViewModel

class WordViewModel: ObservableObject {
    @Published var words:[Word] = []
    let moc = PersistenceController.shared.container.viewContext // Context
    
    init() {
        fetchAllWords()
    }

    func fetchAllWords() -> Void {
        let fetchRequest: NSFetchRequest<Word>
        fetchRequest = Word.fetchRequest()
        words = try! moc.fetch(fetchRequest)
    }

    func getWordsFor(tag:Tag){
        let request: NSFetchRequest<Word> = Word.fetchRequest()
        request.predicate = NSPredicate(format: "ANY wordToTag = %@", tag)
        
        do {
            words = try moc.fetch(request)
            print (words)
        } catch let error{
            print("Error fetching songs \(error)")
        }
    }

I would try to change

@ObservedObject var wordViewModel

with a

@StateObject

As described in this other thread. https://developer.apple.com/forums/thread/727528?answerId=749424022#749424022

hi,

each of TagViewPhone and WordView2 has its own WordViewModel. changing one does not change the other.

  • you should create the WordViewModel as a @StateObject in MainView2, and make it available to the TagViewPhone and WordView2 subviews, either as a parameter (reference the model passed in as an @ObservedObject) or through the environment.

  • should you still have updating problems ... my guess is that you will ... then you may want to explicitly precede any change to a Word or Tag by telling all associated objects (Tags associated with a Word, or Word associated a Tag) to initiate an objectWillChange.send() message. changing attributes of a Tag/Word at one end of a relationship is not seen as a change of any attributes on the other Word/Tag end of the relationship.

hope that helps,

DMG

Update View when viewModel is modified
 
 
Q