Using the @AppStorage Property Wrapper in SwiftUI

Posted in SwiftUI

October 8th, 2021

⏱ Reading Time: 5 mins

There are a few things when developing iOS apps that are simply unavoidable, and all developers deal with them sooner or later. One of those is saving and reading values to and from the user defaults file respectively.

User defaults is actually a property list file (plist) accessible programmatically that can store values of basic datatypes. Since it’s a property list file, stored content is in key-value pairs. The purpose of user defaults is to let us save bits of data and read them back whenever that’s necessary, quickly and effortlessly. This data is available app-wide and can be updated from anywhere inside an app. User defaults is not meant though to be acting as a persistent storage of large data. On the contrary, it’s best for keeping stuff like user preferences or settings. Also, it’s the worst choice a developer can make in order to save sensitive data. User defaults provide no security at all, and it’s easily accessible; so, just don’t do that.

In programming terms now, we all know from the pre-SwiftUI days that we can access user defaults using the UserDefaults class in the Foundation framework. This is still valid nowadays as well. However, with the SwiftUI framework’s coming, there is a new player around; the @AppStorage property wrapper.

@AppStorage is one of the various property wrappers available to use in SwiftUI. We can consider it as an app-wide source of truth, which, however, has one great difference comparing to others; modified values do not remain in memory, but they get written to the user defaults file. Using it for reading and writing values is straightforward, and in this post we’re just about to go through a few concepts around this topic.

Using the @AppStorage property wrapper

Suppose that we’re building an app where we have a user profile section. There, we display some bits of information that users are able to modify in a modal sheet. As an example, see the following simple demonstration:

Display and update the following user preferences: Profile name, age and online status.

Starting from the view that displays the user profile details, the first thing we need is to access the user defaults and fetch each piece of data we need. In order to do that for the profile name, we declare the following property, marked with the @AppStorage property wrapper as so:

The first argument provided above is the key matching to the data we want to retrieve. The second argument points to the users default file that @AppStorage should use. In most cases, the default one is what we’ll want to use, and we specify that passing the .standard value. However, it’s also possible to provide a different user defaults file to use instead:

In any case, you’ll notice that we assign an initial value to the profileName property, and doing so has a double purpose:

  1. To implicitly declare the data type of the value we are trying to read or write.
  2. To specify an initial value to use in case no prior value has been written to user defaults yet.

Indeed, the first time the app will run will show the “Not Set” value, but after users have updated the profile name, their input is what will be appearing from there on.

Besides profileName, here are two more properties marked with the @AppStorage property wrapper that complete the example demonstrated above:

age is an integer value, while status is a value of a custom type called OnlineStatus; that’s an enum with String raw values:

Besides String and Int, other data types that user defaults can store are:

  • Bool
  • Double
  • URL
  • Data

Handling the above properties inside the SwiftUI view, is similar to any other property marked with the @State or other property wrappers:

Simplifying @AppStorage property declarations

The code examples that were previously demonstrated pointed to the user defaults file explicitly passing the .standard value to the store parameter. However, @AppStorage property declarations can be simplified just by omitting to specify the user defaults file to use, and by keeping the keys only:

In case we use the standard user defaults file for storing and reading data, the above will work just fine. If, however, there is a different user defaults file to use, then we can still keep the above, and add the following view modifier to the view that contains those properties marked with the @AppStorage wrapper:

Avoiding errors with keys

As you’ve seen in the previous examples, it’s necessary to provide the key of each piece of data that we want to read from user defaults. However, the more we use @AppStorage properties in an app, the higher the risk to make typo errors in keys. Even though it doesn’t sound so important, it might lead to unnecessary loss of time trying to figure out why reading from user defaults or writing to it doesn’t work properly.

So, in order to avoid all that and make sure that no accidental errors will ever occur while typing key names, I personally prefer to define an enum, cases of which represent the actual keys I’m going to use.

For the current example demonstrated in this post, the following enum is what I’d define:

Notice that the enum’s cases have matching String raw values. Now, we can use those instead of actual keys when declaring the @AppStorage properties:

Following the above approach guarantees that there will be no accidental typo errors that will drive us… crazy and make us spend valuable time with such non-important situations.

Writing to user defaults

With the @AppStorage property wrapper, writing to user defaults file has no difference compared to reading values from it. So, in this example, the view that allows to edit the profile data will be containing same or similar @AppStorage declarations (depending on the case) to those already previously presented:

What actually differs is how we use the above properties. When we pass their binding values as arguments to various SwiftUI views and they get updated, those changes will be written in turn to the user defaults file.

See, for instance, the next code snippet that allows users to edit the profile data; we provide the binding value of the @AppStorage properties everywhere, and on change, @AppStorage property wrapper will store them persistently:

Conclusion

So, that’s all you need to know in order to start using the @AppStorage property wrapper in your SwiftUI projects and store to user defaults. As I’ve mentioned previously, it’s mostly suitable for saving small bits of data, such as user settings or something similar to that. However, it worths noting that many developers prefer implementing their own storing solutions and avoid using user defaults at all. That’s totally up to you, and a decision to make if you realize that @AppStorage either does not satisfy your needs, or it seems to be problematic. In any case, I’m sure that it’s going to be useful in many occasions, so give it a try now that you’ve learnt about it. Thank you for reading, and stay around for new posts that will come soon!

Stay Up To Date

Subscribe to my newsletter and get notifiied instantly when I post something new on SerialCoder.dev.

    We respect your privacy. Unsubscribe at any time.