简体   繁体   中英

SwiftUI best practice for using @AppStorage for settings: How to read the UserDefaults before the @AppStorage loaded?

I use the @AppStorage to save some preference in the settings view, just like apple recommends: https://developer.apple.com/documentation/swiftui/settings

I also have a main window and it will read some preference before user open the settings view and then it seems I will read "wrong" UserDefaults because the expect values are not write to UserDefaults because the settings view is not loaded yet.

For example:

// Settings view
struct SettingsView: View {
   @AppStorage("x") var x = 5  // give preference a default value
   ...
}

// Main view
struct MainView: View {

    func foo() {
        let x = UserDefaults.standard.double(forKey: "x")
        // x is not the default value I expect
    }
}

Possible solutions I know:

  1. On app launch, check if the UserDefaults is nil, if so then write them with default values
  2. When read UserDefaults, check if UserDefaults is nil, if so then read the default values instead.

Are there any better ways to handle it? Thanks!

UserDefaults provides a method called register that loads it with 'default' (no pun intended) values. These values only get used in the event that there aren't any values for those keys actually stored in UserDefaults already.

I find it useful to populate it with values from a plist in the app bundle in my AppDelegate on app launch (such as in didFinishLaunchingWithOptions -- if using the SwiftUI lifecycle, you can still do this: SwiftUI app life cycle iOS14 where to put AppDelegate code? ):

if let prefs = Bundle.main.path(forResource: "defaults", ofType: "plist"), 
    let dict = NSDictionary(contentsOfFile: prefs) as? [String : Any] {
  UserDefaults.standard.register(defaults: dict)
}

https://developer.apple.com/documentation/foundation/userdefaults/1417065-register

From the docs:

The contents of the registration domain are not written to disk; you need to call this method each time your application starts . You can place a plist file in the application's Resources directory and call register(defaults:) with the contents that you read in from that file.

Using this system, you don't have to bother checking yourself for nil values and deciding whether or not to load defaults. And, when you do your check reading from UserDefaults , you'll see the pre-populated value in there.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM