Understanding the Userdefaults Register Method

Understanding the UserDefaults register method

From the documentation for register(defaults:):

If there is no registration domain, one is created using the specified dictionary, and NSRegistrationDomain is added to the end of the search list.

The contents of the registration domain are not written to disk; you need to call this method each time your application starts.

What this means is that the registered defaults act as a fallback to the normal, user defaults that you are working with. The registered defaults do not overwrite or replace the standard user defaults.

When you attempt to lookup a key from UserDefaults.standard, if the key isn't there, then it is looked up in the registered defaults and that result (if any) is returned.

Once you set a value in UserDefaults.standard, of course it is that value that is returned for the key. If you remove the key from UserDefaults.standard then the registered default is used again.

Where to place the .register() method to set an initial/default value for UserDefaults?

This init is called before UIApplication has set up yet, so if you want to register defaults (which can be done from plist of big dictionary) it should be done via app delegate adapter.

class AppDelegate: NSObject, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {

// Register defaults here !!
UserDefaults.standard.register(defaults: [
"selectedRoundLength": 1
// ... other settings
]
)

return true
}
}

@main
struct TestApp: App {

@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate

var body: some Scene {
WindowGroup {
ContentView()
}
}
}

User Defaults is not registering new keys?

I would suspect that your applicationDidFinishLaunching is not invoked. It could be caused, if you are using Storyboards, that your Appdelegate is not hooked up properly.

You can fix it following this asnwer or move the initialisation of your defaults to:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool

what will be called every time the application launches.

Also, Apple even discourages you from using applicationDidFinishLaunching(_:) in this documentation.

Saving Date in UserDefaults of the first time the App was opened


  1. There's no need for register(defaults:).
  2. Don't use value(forKey:) to read data from UserDefaults.

All you need to do is first check if the date has been set, if not, set it.

if let firstOpen = UserDefaults.standard.object(forKey: "FirstOpen") as? Date {
print("The app was first opened on \(firstOpen)")
} else {
// This is the first launch
UserDefaults.standard.set(Date(), forKey: "FirstOpen")
}

Swift UserDefaults

In you are using Swift 2, then what you have to do is just putting your code inside a function/method:

let defaults = NSUserDefaults.standardUserDefaults()
defaults.setBool(false, forKey: "hasStarted")

like so, in:

func applicationWillTerminate( application: UIApplication) {

let defaults = NSUserDefaults.standardUserDefaults()
defaults.setBool(false, forKey: "hasStarted")
}

or here:

func applicationWillResignActive( application: UIApplication) {
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setBool(false, forKey: "hasStarted")
}

I've called it there because you said that you want it to save it when the app is closed.

EXPLANATION:

You called your function outside any function/method's body, which is restricted in swift. You are not allowed to do anything outside methods(just in the body of the class), besides for declaring and initialising variables/constants.

What is the use of -[NSUserDefaults registerDefaults:]?

The difference is that the first code-snippet you register defaults that will be used when the user has not made any changes to the "property".

So if you want to provide let's say a "property" with the key name 'Welcome message', you could instead of having the property returning nil insert a default message 'Welcome first-time user' that will be displayed when there have been no changes to the property.

This will simplify your logic because you don't need to write an if test to check if the "property" returns nil and then make another message if this is the case.

NSString *greeting = [[NSUserDefaults standardUserDefaults] stringForKey:@"Greeting"];

if(greeting == nil) {
NSLog(@"Welcome first-time user!");
}

The second code-snippet you posted is for setting the property to another value. You will have different set methods (setString, setObject, setBoolean) to set values depending on your program state in the Userdefaults.

EDIT-----Updates as requested in comment.

The first method is for registering values to defaults, as the name implies. The first time you access the property with some key name the value will be either nil for objects, false for booleans or 0 for numbers. Instead of doing a lot of tests and so on to so if the values is not set in the program, and then do something "default" action such as the example above, you can ship your application with some already predefined values for these keys.

A typical place to put the registerDefaults is in the initializer-method in the appDelegate.

Then somewhere in your program you may want to set the values of these fields then you use the setObject, setString, setBoolean...and for retrieving you use stringForKey, objectForKey...

Think of it as this

The registerDefaults is the constructor where you may supply sensible values for the object, otherwise you get some defaults which I already wrote. Then later if you want to change the object's attributes you do NOT use the "constructor" but the set/get methods.

How to register user defaults using NSUserDefaults without overwriting existing values?

From the documentation for -registerDefaults: (emphasis added):

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 registerDefaults: with the contents that you read in from that file.

So your code was on the right track. This is how you register default defaults.

I usually use this in -applicationDidFinishLaunching::

// Load default defaults
[[NSUserDefaults standardUserDefaults] registerDefaults:[NSDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Defaults" ofType:@"plist"]]];

Using a plist makes it easy to add and change defaults in your app, and it prevents you from making the mistake of using @"NO" as a value too.

Edit: Swift 3 variant:

 UserDefaults.standard.register(defaults: NSDictionary(contentsOf: Bundle.main.url(forResource: "Defaults", withExtension: "plist")!)! as! [String : Any])


Related Topics



Leave a reply



Submit