Search code examples
swiftuiappstorage

Reverting @AppStorage when dismissing a preference view via cancel


Consider the following PreferencesView presented as a sheet:

struct PreferencesView: View {
    @Environment(\.dismiss) private var dismiss

    @AppStorage("setting1") var setting1: Bool = true
    @AppStorage("setting2") var setting2: Bool = false
    @AppStorage("setting3") var setting3: Bool = false
    // many more

    var body: some View {
        NavigationView {
            Form {
                Toggle("Setting 1", isOn: $setting1)
                Toggle("Setting 2", isOn: $setting2)
                Toggle("Setting 3", isOn: $setting3)
            }
            .navigationTitle("Preferences")
            .toolbar {
                ToolbarItem(placement: .navigationBarLeading) {
                    Button("Cancel") {
                        reset()

                        dismiss()
                    }
                }
                ToolbarItem(placement: .navigationBarTrailing) {
                    Button("Save") {
                        dismiss()
                    }
                }
            }
        }
    }

    private func reset() {
        // what do I put here?
    }
}

If the user hits cancel, I'd like the value of the settings be set back to when the sheet opened. I could add separate initialSetting1, etc properties, and then in the reset function add setting1 = initialSetting1. But with a lot of settings that becomes tedious.

Is there another way?


Solution

    • Load current settings using onAppear
    • Use local @State to bind to a toggle
    • Save current values from @State to settings when user presses Save button.

    Like this:

    struct PreferencesView: View {
        @Environment(\.dismiss) private var dismiss
    
        @AppStorage("setting1") var setting1: Bool = true
        @AppStorage("setting2") var setting2: Bool = false
        @AppStorage("setting3") var setting3: Bool = false
        
        // Initial value doesn't matter, will be overwritten
        @State var currentSetting1: Bool = false
        @State var currentSetting2: Bool = false
        @State var currentSetting3: Bool = false
        // many more
    
        var body: some View {
            NavigationView {
                Form {
                    // Change internal state, not settings
                    Toggle("Setting 1", isOn: $currentSetting1)
                    Toggle("Setting 2", isOn: $currentSetting2)
                    Toggle("Setting 3", isOn: $currentSetting3)
                }
                .navigationTitle("Preferences")
                .toolbar {
                    ToolbarItem(placement: .navigationBarLeading) {
                        Button("Cancel") {
                            // No need to do anything here
                            dismiss()
                        }
                    }
                    ToolbarItem(placement: .navigationBarTrailing) {
                        Button("Save") {
                            // Save current internal state to settings
                            setting1 = currentSetting1
                            setting2 = currentSetting2
                            setting3 = currentSetting3
                            dismiss()
                        }
                    }
                }
                .onAppear() {
                    // Load settings
                    currentSetting1 = setting1
                    currentSetting2 = setting2
                    currentSetting3 = setting3
                }
            }
        }
    }