What the best way to achieve @UserDefaults @State?

Fascinatingly, the above code works perfectly if I use the iOS-style toggle (on macOS):

        Toggle(isOn: $usePaperLook) { Text("Use Paper Texture") }
                    .toggleStyle(CheckboxToggleStyle())

But it doesn’t work for TextField either — same problem, the default gets read and written but the user input doesn’t draw after hitting ‘return’.

As another datapoint this questioner on StackOverflow had the same kind of problem that I’m seeing — the widget kept reverting its state to its original state.

Came across this: "My NSUserDefaults wrapper called Defaults now includes a SwiftUI property wrapper that is a drop-in replacement for @<200b>State. It will trigger view updates when the user defaults item changes."

1 Like

@​AppStorage takes care of the need now

:star_struck:

3 Likes

All above way will make @State absent from reactive. Here is an example

@propertyWrapper

struct UserDefault {
let key: String
let defaultValue: Value

init(wrappedValue value: Value, key: String) {
    self.defaultValue = value
    self.key = key
}

var wrappedValue: Value {
    get { UserDefaults.standard.object(forKey: key) as? Value ?? defaultValue }
    nonmutating set { UserDefaults.standard.set(newValue, forKey: key) }
}
var projectedValue: Binding<Value> {
    Binding<Value>( get: { self.wrappedValue }, set: { newValue in self.wrappedValue = newValue } )
}

}

struct TogetherWithStateDemo: View {
@State @UserDefault(key: "booletstKey") var toogleValue: Bool = true
@State @UserDefault(key: "stringTestKey") var stringValue: String = "Default Value"

 public var body: some View {
    VStack {
        Toggle(isOn: $toogleValue[dynamicMember: \.wrappedValue]) {
              Text("Use Paper Texture")
        }
        Text(stringValue)
        
        Button("Change string value") {
            stringValue = "Changed string value"
        }
    }
}

}

Now, if you click the button, the text is not updated any more