UserDefaults Binding with Toggle in SwiftUI UserDefaults Binding with Toggle in SwiftUI ios ios

UserDefaults Binding with Toggle in SwiftUI


Update

------- iOS 14: -------

Starting iOS 14, there is now a very very simple way to read and write to UserDefaults.

Using a new property wrapper called @AppStorage

Here is how it could be used:

import SwiftUIstruct ContentView : View {    @AppStorage("settingActivated") var settingActivated = false    var body: some View {        NavigationView {            Form {                Toggle(isOn: $settingActivated) {                    Text("Setting Activated")                }            }.navigationBarTitle(Text("Settings"))        }    }}

That's it! It is so easy and really straight forward. All your information is being saved and read from UserDefaults.

-------- iOS 13: ---------

A lot has changed in Swift 5.1. BindableObject has been completely deprecated. Also, there has been significant changes in PassthroughSubject.

For anyone wondering to get this to work, below is the working example for the same. I have reused the code of 'gohnjanotis' to make it simple.

import SwiftUIimport Combinestruct ContentView : View {    @ObservedObject var settingsStore: SettingsStore    var body: some View {        NavigationView {            Form {                Toggle(isOn: $settingsStore.settingActivated) {                    Text("Setting Activated")                }            }.navigationBarTitle(Text("Settings"))        }    }}class SettingsStore: ObservableObject {    let willChange = PassthroughSubject<Void, Never>()    var settingActivated: Bool = UserDefaults.settingActivated {        willSet {            UserDefaults.settingActivated = newValue            willChange.send()        }    }}extension UserDefaults {    private struct Keys {        static let settingActivated = "SettingActivated"    }    static var settingActivated: Bool {        get {            return UserDefaults.standard.bool(forKey: Keys.settingActivated)        }        set {            UserDefaults.standard.set(newValue, forKey: Keys.settingActivated)        }    }}


With help both from this video by azamsharp and this tutorial by Paul Hudson, I've been able to produce a toggle that binds to UserDefaults and shows whichever change you've assigned to it instantaneously.

  • Scene Delegate:

Add this line of code under 'window' variable

var settingsStore = SettingsStore()

And modify window.rootViewController to show this

window.rootViewController = UIHostingController(rootView: contentView.environmentObject(settingsStore))
  • SettingsStore:
import Foundationclass SettingsStore: ObservableObject {    @Published var isOn: Bool = UserDefaults.standard.bool(forKey: "isOn") {        didSet {            UserDefaults.standard.set(self.isOn, forKey: "isOn")        }    }}
  • SettingsStoreMenu

If so you wish, create a SwiftUI View called this and paste:

import SwiftUIstruct SettingsStoreMenu: View {        @ObservedObject var settingsStore: SettingsStore        var body: some View {        Toggle(isOn: self.$settingsStore.isOn) {            Text("")        }    }}
  • Last but not least

Don't forget to inject SettingsStore to SettingsStoreMenu from whichever Main View you have, such as

import SwiftUIstruct MainView: View {            @EnvironmentObject var settingsStore: SettingsStore    @State var showingSettingsStoreMenu: Bool = false        var body: some View {        HStack {            Button("Go to Settings Store Menu") {                    self.showingSettingsStoreMenu.toggle()            }            .sheet(isPresented: self.$showingSettingsStoreMenu) {                    SettingsStoreMenu(settingsStore: self.settingsStore)            }        }    }}

(Or whichever other way you desire.)


This seam to work well :

enum BackupLocalisations: String, CaseIterable, Hashable, Identifiable {    case iPhone = "iPhone"    case iCloud = "iCloud"        var name: String {        return self.rawValue    }    var id: BackupLocalisations {self}}enum Keys {    static let iCloudIsOn = "iCloudIsOn"    static let backupLocalisation = "backupLocalisation"    static let backupsNumber = "backupsNumber"}
    class SceneDelegate: UIResponder, UIWindowSceneDelegate {        var window: UIWindow?    var settings = Settings()…/…    let contentView = ContentView()            .environmentObject(settings)… }
class Settings: ObservableObject {    @Published var iCloudIsOn: Bool = UserDefaults.standard.bool(forKey: Keys.iCloudIsOn) {        didSet { UserDefaults.standard.set(self.iCloudIsOn, forKey: Keys.iCloudIsOn) }    }        @Published var backupLocalisation: String = UserDefaults.standard.object(forKey: Keys.backupLocalisation) as? String ?? "iPhone" {        didSet { UserDefaults.standard.set(self.backupLocalisation, forKey: Keys.backupLocalisation) }    }        @Published var backupsNumber: Int = UserDefaults.standard.integer(forKey: Keys.backupsNumber) {        didSet { UserDefaults.standard.set(self.backupsNumber, forKey: Keys.backupsNumber) }    }}
struct ContentView: View {    @ObservedObject var settings: Settings    var body: some View {        NavigationView {            Form {                Section(footer: Text("iCloud is \(UserDefaults.standard.bool(forKey: Keys.iCloudIsOn) ? "on" : "off")")) {                    Toggle(isOn: self.$settings.iCloudIsOn) { Text("Use iCloud") }                }                Section {                    Picker(selection: $settings.backupLocalisation, label: Text("\(self.settings.backupsNumber) sauvegarde\(self.settings.backupsNumber > 1 ? "s" : "") sur").foregroundColor(Color(.label))) {                        ForEach(BackupLocalisations.allCases) { b in                            Text(b.name).tag(b.rawValue)                        }                    }                                        Stepper(value: self.$settings.backupsNumber) {                        Text("Nombre de sauvegardes")                    }                }            }.navigationBarTitle(Text("Settings"))                    }    }}
struct ContentView_Previews: PreviewProvider {    static var previews: some View {        ContentView().environmentObject(Settings())    }}

Xcode 11.3.1