20. Apr 2023iOS

Exploring our iOS toolbox - How to save values to UserDefaults & KeyChain in Swift

Swift UserDefaults is a simple and lightweight way to store small amounts of data in your application, such as user preferences, settings, and application state. However, working with UserDefaults and Keychain can be cumbersome, this is where the GoodPersistence swift package comes in.

Marek VricaniOS Developer

Storing data in UserDefaults

We use the UserDefaultValue class, which is a property wrapper that enables the storage of any Codable conforming value in UserDefaults.

To use this class, you can simply create an instance of the UserDefaultValue class and pass in the key and default value for the property wrapper.

For example, if you want to store a user's name in UserDefaults, you can create an UserDefaultValue instance like this:

@UserDefaultValue("userName", defaultValue: "Guest")
var userName: String

In the example above, the key "userName" will be used to store the user's name in UserDefaults. If no value for this key exists, the default value "Guest" will be used instead.

This property wrapper also provides another useful function, observing changes using Combine.

🔐 Storing data in KeyChain

If you’ve ever struggled with storing sensitive data inside the Keychain, with GoodPersistence package it’s never been easier.

To save values inside the KeyChain we use another wrapper, called KeychainValue . Let me show you how does it work:

struct UserSensitiveInformation {

	let userId: String
	let dateOfBirth: Date
	let address: String


		String(describing: UserSensitiveInformation.self),
		defaultValue: nil
		accessibility: .afterFirstUnlockThisDeviceOnly
var userSensitiveInformation: UserSensitiveInformation?

For example above we have a struct called UserSensitiveInformation which holds all of the sensitive data that we want to securely store inside the Keychain.

As you can see, the implementation is quite similar to the previous example that uses UserDefaults.

We declare a key and its default value, but in addition to that, there is a accessibility parameter that we can also specify. This parameter has a default value  nil, which means that you can leave it empty if needed.

The accessibility parameter determines when the stored data is accessible. In the given code, the value is set to .afterFirstUnlockThisDeviceOnly, indicating that the data can only be accessed from the keychain once the device is unlocked.

Listen to value changes

In addition to the primary purpose of storing values,  UserDefaultValue and KeychainValue wrappers also offer a convenient feature for observing value changes using Combine framework.

Let’s say we want to receive updates whenever the userName value changes.

After declaring the userName variable using the UserDefaultValue the wrapper as shown in the first example, you can easily declare a publisher for it using the Combine framework.

Here's an example of how to do that:

lazy var userNamePublisher = _userName

This publisher emits a new value whenever the userName the variable is changed. To subscribe to updates from the publisher, we can use the sink or assign method, which receives a new value whenever the userName variable changes.

In this example, we simply set the text of a label inside the assing method:

var cancellable: AnyCancellable?
let userNameLabel = UILabel()

cancellable = userNamePublisher
			.assing(to: \\.text, on: userNameLabel, ownership: .weak)

These examples also works for KeychainValue wrapper. No additional setup is needed.


You have gone through the process of storing data using the GoodPersistence package.

Now you may be wondering how it works under the hood of the app. Luckily, the package comes with a sample that you can explore to gain a deeper understanding of its functionality.

If you found this package useful, be sure to check out our other packages. Who knows, you might just find another gem that can help take your app to the next level!

Marek VricaniOS Developer