I have a settings view that has a button which toggles a binding that's stored with UserDefaults.
struct Settings: View {
@ObservedObject var settingsVM = SetttingsViewModel()
var body: some View {
if settingsVM.settingActivated {
Text("Setting activated")
} else {
Text("Setting deactivated")
}
Button("Activate") {
settingsVM.settingActivated.toggle()
}
}
}
SettingsViewModel
class SetttingsViewModel: ObservableObject {
@Published var settingActivated: Bool = UserDefaults.standard.bool(forKey: "settingActivated") {
didSet {
UserDefaults.standard.set(self.settingActivated, forKey: "settingActivated")
}
}
}
The text("Setting activated/ Setting deactivated")in the Settings view update instantly when i press the button but the text in ContentView doesn't change unless i restart the app & i have no idea why.
struct ContentView: View {
@ObservedObject var settingsVM = SetttingsViewModel()
@State private var showsettings = false
var body: some View {
if settingsVM.settingActivated {
Text("Setting Activated")
.padding(.top)
} else {
Text("Setting Deactivated")
.padding(.top)
}
Button("Show Settings") {
showsettings.toggle()
}
.sheet(isPresented: $showsettings) {
Settings()
}
.frame(width: 300, height: 300)
}
}
This is for a macOS 10.15 app so i can't use @AppStorage
CodePudding user response:
Right now, you don't have any code in you view model to react to a change in UserDefaults. Meaning, if UserDefaults
gets a new value set, it won't know about it. And, since you're using a different instance of SettingsViewModel
in your two different views, they can easily become out-of-sync.
The easiest change would be to pass the same instance of SettingsViewModel
to Settings
:
struct Settings: View {
@ObservedObject var settingsVM: SettingsViewModel //<-- Here
var body: some View {
if settingsVM.settingActivated {
Text("Setting activated")
} else {
Text("Setting deactivated")
}
Button("Activate") {
settingsVM.settingActivated.toggle()
}
}
}
struct ContentView: View {
@ObservedObject var settingsVM = SetttingsViewModel()
@State private var showsettings = false
var body: some View {
if settingsVM.settingActivated {
Text("Setting Activated")
.padding(.top)
} else {
Text("Setting Deactivated")
.padding(.top)
}
Button("Show Settings") {
showsettings.toggle()
}
.sheet(isPresented: $showsettings) {
Settings(settingsVM: settingsVM) //<-- Here
}
.frame(width: 300, height: 300)
}
}
Another option would be to use a custom property wrapper (like AppStorage
, but available to earlier targets): https://xavierlowmiller.github.io/blog/2020/09/04/iOS-13-AppStorage
Also, @vadian's comment is important -- if you had access to it, you'd want to use @StateObject
. But, since you don't, it's important to store your ObservableObject
at the top level so it doesn't get recreated.