It does not read that the setting has changed

Viewed 36

I have a problem I change the "isDynamic" setting in the "SettingView" and I exit the setting window and the "SongbookView" does not register that the setting has changed. I want to change the search engine depending on what option is selected in the settings. What is the cause of this situation?

SongbookView:

import CoreData
import SwiftUI

struct SongbookView: View {
    @State var searchText: String = ""
    @State var isSettings: Bool
    @ObservedObject var userSettings: UserSettings = UserSettings()
    @Environment(\.managedObjectContext) var managedObjectContext
    @FetchRequest(
        entity: Song.entity(),
        sortDescriptors: [NSSortDescriptor(keyPath: \Song.number, ascending: true)]
    ) var songs: FetchedResults<Song>
    
    var body: some View {
        NavigationView{
            VStack{
                if userSettings.isDynamic == false {
                    SearchBar(text: $searchText)
                } else {
                    DynamicSearchBar(text: $searchText)
                }
                List(songs.filter({searchText.isEmpty ? true : removeNumber(str: $0.content!.lowercased()).contains(searchText.lowercased()) || String($0.number).contains(searchText)}), id:\.objectID) { song in
                    NavigationLink(destination: DetailView(song: song, isSelected: song.favorite)) {
                        HStack{
                            Text("\(String(song.number)). ") .font(.headline) + Text(song.title ?? "Brak tytułu")
                            if song.favorite {
                                Spacer()
                                Image(systemName: "heart.fill")
                                    .accessibility(label: Text("To jest ulubiona pieśń"))
                                    .foregroundColor(.red)
                            }
                        }.lineLimit(1)
                    }
                }.id(UUID())
                    .listStyle(InsetListStyle())
            }
            .padding(.top, 10)
            .navigationBarTitleDisplayMode(.inline)
            .toolbar {
                ToolbarItem(placement: .principal) {
                    HStack{
                        Text(String(self.userSettings.isDynamic))
                        Spacer()
                        Text("Śpiewnik")
                            .font(.system(size: 20))
                            .bold()
                        Spacer()
                        Button(action: {
                            isSettings.toggle()
                            print(userSettings.isDynamic)
                        }) {
                            Image(systemName: "gearshape")
                                .resizable()
                                .frame(width: 16.0, height: 16.0)
                        }
                        .sheet(isPresented: $isSettings) {
                            SettingView(isPresented: $isSettings)
                                }
                    }
                }
            }
        }
    }
    
    func removeNumber(str: String) -> String {
        var result = str
        let vowels: Set<Character> = ["1", "2", "3", "4", "5", "6", "7", "8", "9"]
        result.removeAll(where: { vowels.contains($0) })
        return result
    }
}

SettingView:

import SwiftUI

struct SettingView: View {
    @ObservedObject var userSettings = UserSettings()
    @Binding var isPresented: Bool
    var body: some View {
        NavigationView {
            Form {
                Toggle("Dynamiczna wyszukiwarka", isOn: $userSettings.isDynamic)
                    .onChange(of: userSettings.isDynamic) { value in
                        print(value)
                    }
                Button(action: {
                    print(userSettings.isDynamic)
                    isPresented = false
                }) {
                    Text("Test")
                }
            }
            .navigationBarTitle("Ustawienia")
        }
    }
}

UserSettings:

import Foundation
import Combine

class UserSettings: ObservableObject {
    @Published var isDynamic: Bool {
        didSet {
            UserDefaults.standard.set(isDynamic, forKey: "isSearchDynamic")
        }
    }
    
    init() {
        self.isDynamic = UserDefaults.standard.object(forKey: "isSearchDynamic") as? Bool ?? false
    }
}
1 Answers

You're using two different instances of UserSettings. When you update isDynamic on one of those instances, the other, even though it has a reference to UserDefaults has no reason to know that it needs to update.

The easiest solution here is to share a single instance of UserSettings:

struct SongbookView: View {
    @State var searchText: String = ""
    @State var isSettings: Bool
    @ObservedObject var userSettings: UserSettings = UserSettings()
    @Environment(\.managedObjectContext) var managedObjectContext
    @FetchRequest(
        entity: Song.entity(),
        sortDescriptors: [NSSortDescriptor(keyPath: \Song.number, ascending: true)]
    ) var songs: FetchedResults<Song>
    
    var body: some View {
        //...
        .sheet(isPresented: $isSettings) {
          SettingView(userSettings: userSettings,isPresented: $isSettings)
        }
        //...
    }
}

struct SettingView: View {
    @ObservedObject var userSettings : UserSettings
    @Binding var isPresented: Bool
    var body: some View {
        //...
    }
}

You could also look into property wrappers like @AppStorage (https://www.hackingwithswift.com/quick-start/swiftui/what-is-the-appstorage-property-wrapper) that actually update dynamically when the UserDefaults values change.

Related