View Update Failure

Hello,

The appearance of one of my buttons is not updating after being tapped. I have had no trouble doing the exact same thing in my other views, but here it simply doesn't work. The "heart" button will keep its original appearance (in this case an unfilled heart) no matter what, despite my print statements indicating that the value has changed. The other actions performed when tapping the heart work perfectly. I've been at it for hours.

Any help would be appreciated. Thanks!

import SwiftUI

struct SearchView: View {
    @State private var searchText = ""
    @StateObject var save = SaveWords()
    @State var heart: String?
    @State var disappear = false
    @State var done = true

    var body: some View {
        NavigationView {
            VStack {
                if !disappear {
                    SearchBarView(text: $searchText)
                    Spacer()
                }
                if searchText != "" {
                    List(.constant(Array(FetchWord.getWordFromStart(start: searchText)).prefix(10).map {Word(word: $0.1)})) { suggestion in
                        NavigationLink(destination: suggestion.IPA.wrappedValue == "error" ?  AnyView(EmptyView()) : AnyView(PracticeView(wordSheet: suggestion)
                            .onAppear {
                                disappear = true
                                if done {
                                    SaveWords.file = "Favorites"
                                    DispatchQueue.main.async {
                                        Task {
                                            try? await save.load()
                                            heart = save.words.contains(suggestion.wrappedValue) ? ".fill" : ""
                                        }
                                    }
                                }
                            }
                            .onDisappear {
                                disappear = false
                                Task {
                                    SaveWords.file = "History"
                                    try? await save.load()
                                    if save.words.first != suggestion.wrappedValue {
                                        save.words.insert(suggestion.wrappedValue, at: 0)
                                        try? await save.save()
                                    }
                                }
                            }
                            .toolbar {
                                ToolbarItem(placement: .navigationBarTrailing) {
                                    Button(action: {
                                        SaveWords.file = "Favorites"
                                        done = false
                                        if heart == "" {
                                            heart = ".fill"
                                            DispatchQueue.main.async {
                                                Task {
                                                    try? await save.load()
                                                    if !save.words.contains(suggestion.wrappedValue) {
                                                        save.words.insert(suggestion.wrappedValue, at: 0)
                                                        try? await save.save()
                                                    }
                                                }
                                            }
                                        } else {
                                            heart = ""
                                            DispatchQueue.main.async {
                                                Task {
                                                    try? await save.load()
                                                    try? await save.delete(wordToDelete: suggestion.wrappedValue)
                                                    try? await save.save()
                                                }
                                            }
                                        }
                                        done = true
                                    }, label: {
                                        Image(systemName: "heart" + (heart ?? ""))
                                    })
                                }
                            })
                        ) {
                            if suggestion.IPA.wrappedValue != "error" {
                                CardView(wordSheet: suggestion)
                            }
                        }
                    }
                }
                
            }
            Spacer()
        }
    }
}

#Preview {
    SearchView()
}

I also experienced a similar problem in the past.

How about trying this way?

class SaveWords: ObservableObject {
    static let shared = SaveWords()
    
    // others...
}
import SwiftUI

struct SearchView: View {
    @State private var searchText = ""
    var save = SaveWords.shared
    @State var heart: String?
    @State var disappear = false
    @State var done = true

    var body: some View {
        NavigationView {
            VStack {
                if !disappear {
                    SearchBarView(text: $searchText)
                    Spacer()
                }
                if searchText != "" {
                    List(.constant(Array(FetchWord.getWordFromStart(start: searchText)).prefix(10).map {Word(word: $0.1)})) { suggestion in
                        NavigationLink(destination: suggestion.IPA.wrappedValue == "error" ?  AnyView(EmptyView()) : AnyView(PracticeView(wordSheet: suggestion)
                            .onAppear {
                                disappear = true
                                if done {
                                    SaveWords.file = "Favorites"
                                    DispatchQueue.main.async {
                                        Task {
                                            try? await save.load()
                                            heart = save.words.contains(suggestion.wrappedValue) ? ".fill" : ""
                                        }
                                    }
                                }
                            }
                            .onDisappear {
                                disappear = false
                                Task {
                                    SaveWords.file = "History"
                                    try? await save.load()
                                    if save.words.first != suggestion.wrappedValue {
                                        save.words.insert(suggestion.wrappedValue, at: 0)
                                        try? await save.save()
                                    }
                                }
                            }
                            .toolbar {
                                ToolbarItem(placement: .navigationBarTrailing) {
                                    Button(action: {
                                        SaveWords.file = "Favorites"
                                        done = false
                                        if heart == "" {
                                            heart = ".fill"
                                            DispatchQueue.main.async {
                                                Task {
                                                    try? await save.load()
                                                    if !save.words.contains(suggestion.wrappedValue) {
                                                        save.words.insert(suggestion.wrappedValue, at: 0)
                                                        try? await save.save()
                                                    }
                                                }
                                            }
                                        } else {
                                            heart = ""
                                            DispatchQueue.main.async {
                                                Task {
                                                    try? await save.load()
                                                    try? await save.delete(wordToDelete: suggestion.wrappedValue)
                                                    try? await save.save()
                                                }
                                            }
                                        }
                                        done = true
                                    }, label: {
                                        Image(systemName: "heart" + (heart ?? ""))
                                    })
                                }
                            })
                        ) {
                            if suggestion.IPA.wrappedValue != "error" {
                                CardView(wordSheet: suggestion)
                            }
                        }
                    }
                }
                
            }
            Spacer()
        }
    }
}

If you’ve confirmed the value is changing when you expect, I would suggest making sure the value is correct — are you ending up with heart.fill, or maybe it’s something else?

I figured it out by myself. Thank you for trying to help though!

View Update Failure
 
 
Q