Setting default values for NSUserDefaults

Swift UserDefaults

UserDefaults (NSUserDefaults) is a go-to database for saving users' preferences over application behavior, e.g., font size, sound disable/enable. So, most of the time, you want each of them to have a default value.

Basic approach #

Let say your app allows users to enable/disable sound and vibration. If you want to have both of them turn on by default, you might do something like this.

let userDefaults = UserDefaults.standard

if userDefaults.value(forKey: "enabledSound") == nil { // 1
userDefaults.set(true, forKey: "enabledSound") // 2
}

// 3
if userDefaults.value(forKey: "enabledVibration") == nil {
userDefaults.set(true, forKey: "enabledVibration")
}

<1> First, you get a value from your preference key. Notice that we use .value(forKey:) not bool(forKey:) because bool(forKey:) will return false if the value is nil, so you can differentiate between nil and false.
<2> If the value is nil, we set a default value of true.
<3> The same applies to enabledVibration.

This might be good enough for your case, but it quite verbose. You have to repeat this for every option that you have.

The right approach with register(defaults:) #

What you might not aware of is you don't even have to come up with anything fancy. UserDefaults has a register(defaults:) method for a situation like this.

To use register(defaults:), you need to specify a dictionary of preference keys and their default value. In the following example, we set both enabledSound and enabledVibration a default value of true.

let userDefaults = UserDefaults.standard
userDefaults.register(
defaults: [
"enabledSound": true,
"enabledVibration": true
]
)

userDefaults.bool(forKey: "enabledSound") // true

The code is a lot cleaner and cleaner. That's all you need to do to set a default value for your UserDefaults. There are some behaviors of register(defaults:) that I want to point out for you.

The value is only set if the key is nil #

If a user sets any preference key, register(defaults:) won't override that key. This is quite obvious behavior for a method that works to set a default value, but I want to assure you that you can safely call register(defaults:) from anywhere and as many as you want.

let userDefaults = UserDefaults.standard

userDefaults.set(false, forKey: "enabledSound") // 1

userDefaults.register(
defaults: [
"enabledSound": true, // 2
"enabledVibration": true
]
)

userDefaults.bool(forKey: "enabledSound") // false

<1> We explicit set enabledSound to false.
<2> The value true that we set with register(defaults:) won't take effect on this, and the value remains false.

The default values are not persist #

The following statement is coming from the Apple documentation of UserDefaults.

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

So, if you have this code in your application(_:didFinishLaunchingWithOptions:).

let userDefaults = UserDefaults.standard

print(userDefaults.integer(forKey: "counter")) // print 0

userDefaults.register(
defaults: [
"counter": 5
]
)

print(userDefaults.integer(forKey: "counter")) // print 5

On the first run, it will print out 0 then 5. On the second run, you might expect it to print 5 and 5, but that's not the case. It still prints out 0 and 5.

The value that is set through register(defaults:) method isn't actually written to disk until you explicitly set it with set(_:forKey:).. That means you need to call register(defaults:) each time your application starts to get the right behavior.

Read the value from plist #

Since plist is a serialization of a dictionary, you can pack your default values into a plist file and call register(defaults:) with the contents from that file. You can modify your default values without touching your codebase this way.

To do that:

  1. Create a plist file with associated key and default value.
2. Read the plist file into a dictionary.
private func readPropertyList() -> [String: Any]? {
guard let plistPath = Bundle.main.path(forResource: "DefaultValues", ofType: "plist"),
let plistData = FileManager.default.contents(atPath: plistPath) else {
return nil
}

return try? PropertyListSerialization.propertyList(from: plistData, format: nil) as? [String: Any]
}
  1. Register is like we did in the previous example.
let userDefaults = UserDefaults.standard

if let defaultValues = readPropertyList() {
userDefaults.register(defaults: defaultValues)
}

That's everything you need to know about registering default values to UserDefaults.


Feel free to follow me on Twitter and ask your questions related to this post. Thanks for reading and see you next time.

If you enjoy my writing, please check out my Patreon https://www.patreon.com/sarunw and become my supporter. Sharing the article is also greatly appreciated.

Become a patron Tweet Share

If you enjoy this article, you can subscribe to the weekly newsletter.

Every Friday, you’ll get a quick recap of all articles and tips posted on this site — entirely for free.

← Home