Give your simulator superpowers

Click here to
Build Apps Faster

Using User Defaults to store preferences in Swift

User Defaults are the go-to solution for Swift applications to store preferences that persist across launches of your app. It’s a key-value store backed by a property list (plist) file. Due to this type of backing store, you need to be aware of the supported storage types.

There are a few best practices when working with User Defaults. I can also recommend specific solutions based on implementation experience from using it in tens of apps. Let’s dive in!

What are User Defaults?

Apps commonly use User Defaults to store users’ preferences. You can store preferences like the user’s favorite stocks or save specific user states like “user has seen the onboarding.”

The code to store preferences like these could look as follows:

UserDefaults.standard.set(true, forKey: "has-seen-onboarding")
UserDefaults.standard.set(["AAPL", "TSLA"], forKey: "favorite-stocks")

print(UserDefaults.standard.bool(forKey: "has-seen-onboarding")) 
// Prints: true
print(UserDefaults.standard.array(forKey: "favorite-stocks")) 
// Prints: ["AAPL", "TSLA"]

In this case, we’re using the standard user defaults container. In most cases, this will be sufficient. However, you might want to consider using group user defaults.

Sharing User Defaults with other apps and extensions

Using so-called app groups, you can share the User Defaults container with other apps and extensions. I highly recommend using this technique for any app from the start. Even though there might not be a need to share preferences right now, you’ll thank yourself later if you add extensions that need to read or write preferences from the main app.

To configure App Groups you need to add a new capability to your project’s settings:

You can start sharing User Defaults with other apps and extensions by adding the App Groups capability.
You can start sharing User Defaults with other apps and extensions by adding the App Groups capability.

You can find detailed instructions inside Apple’s documentation. Once configured, you can create a new user defaults instance using the group identifier:

extension UserDefaults {
    static let group = UserDefaults(suiteName: "group.your.identifier")

You can now access the shared group container anywhere by making use of the static property:["AAPL", "TSLA"], forKey: "favorite-stocks")

Any app or extension configured with the same app group will now be able to read and write the favorite stocks. I’m using this technique inside Stock Analyzer to populate widgets based on favorite stocks configured in the main application.

The types of data User Defaults support

Property lists must support the objects you store inside User Defaults. You’ll run into the following error as soon as you write an unsupported object:

*** Terminating app due to uncaught exception ‘NSInvalidArgumentException’, reason: ‘Attempt to insert non-property list object UserDefaults.Stock(symbol: “AAPL”) for key last-opened-stock’

In this case, I tried to store an encodable object:

struct Stock: Decodable {
    let symbol: String
} "AAPL"), forKey: "last-opened-stock")

Anytime you run into an exception like this, you must convert the data before storing it. You can use a JSONEncoder to encode the instance to data and decode it when you read the value.

By default, User Defaults support the following types:

  • Data
  • Strings
  • Numbers (NSNumber)
  • Dates
  • Arrays
  • Dictionaries

If your type is not on this list, you need to find a way to convert it to any of the supported types.

Responding to changes

While you can use the didChangeNotification to observe for changes, I recommend looking into managed solutions like this User Defaults Property Wrapper.

Exploring the backing storage

As an app developer, keeping an eye on stored data is essential. Even though you might think you’re in control, there could be preferences data stored by 3rd party libraries. To ensure no unexpected data is stored, I like to open the User Defaults from an application installed on the Simulator.

To do this, I’m making use of RocketSim which allows me to access the backing storage quickly:

You can access the User Defaults backing store by using RocketSim.
You can access the User Defaults backing store by using RocketSim.

For example, I could explore the preferences for Stock Analyzer:

The plist file contains the User Defaults key values for Stock Analyzer.
The plist file contains the User Defaults key values for Stock Analyzer.

In this case, the application does not yet contain any favorite stocks. There are a few other expected properties, but nothing I should worry about. Great!

Alternatives considered

User Defaults are a great solution in most cases, but you might want to explore other solutions in case you’re storing sensitive data or when you want to access data across devices.

Keychain for security

User Defaults are not secure enough to store sensitive data. User credentials, API keys, or other sensitive data should be stored inside the keychain instead.

CloudKit for cross platform

Consider using the NSUbiquitousKeyValueStore in case you want the preferences to be accessible from other Apple devices with your app installed. It’s a similar key-value store but uses iCloud as a backing store.


You can store preferences using User Defaults and capture state across app launches. App Groups are great for sharing preferences with other apps and extensions, and you need to keep an eye on the types of data you can store. By monitoring the backing store, you’ll ensure no unexpected stored data. When you need to access data across devices or when you want to store sensitive data, it’s better to look at alternatives.

If you like to improve your Swift knowledge, even more, check out the Swift category page. Feel free to contact me or tweet to me on Twitter if you have any additional tips or feedback.