How to use @AppStorage in SwiftUI | SwiftUI Bootcamp #52
@State disappears when the app closes. @AppStorage persists. It's SwiftUI's built-in wrapper around UserDefaults that makes saving and loading small pieces of data — user preferences, settings, login state, names — a single line of code. After this lesson you'll know how to store and retrieve values that survive app launches, and understand the types and key naming conventions that make @AppStorage safe to use.
What You'll Learn
- How
@AppStorageconnects a property to aUserDefaultskey automatically - Which types
@AppStoragesupports natively (String, Int, Double, Bool, Data, URL) - How to use an optional
@AppStorageproperty to distinguish "never set" from a default value - How changes to
@AppStorageautomatically trigger SwiftUI re-renders, just like@State
Mental Model
Think of @AppStorage as a sticky note attached to the app's front door. When the app closes, the sticky note stays on the door. When the app opens again, the note is still there. @State is like writing a note on your hand — useful while you're awake, but gone when you wake up the next morning.
The key string "name" is the label on the sticky note. If two views use the same label (@AppStorage("name")), they're reading and writing the same note. If they use different labels, they're different notes. This is both @AppStorage's power (same key = shared value across views) and its main risk (accidentally reusing a key name can overwrite unrelated data).
Detailed Explanation
@AppStorage is a property wrapper that reads from and writes to UserDefaults.standard using a string key. Reading the property retrieves the current value from UserDefaults. Writing to the property updates UserDefaults and triggers a SwiftUI re-render — exactly like @State would, except the change is also persisted to disk.
Supported value types are: String, Int, Double, Bool, Data, and URL. For custom types, you must encode them to Data first (using JSONEncoder or Codable). Trying to use an unsupported type directly with @AppStorage produces a compile error.
Optional @AppStorage is a powerful pattern: @AppStorage("name") var currentUserName: String?. This starts as nil when the key has never been set, which lets you distinguish between "the user has not provided a name yet" vs. "the user set their name to an empty string." When you want a default fallback value, use @AppStorage("name") var currentUserName: String = "Guest" — this returns "Guest" if the key doesn't exist in UserDefaults.
Because all views that share the same @AppStorage key see the same UserDefaults value, @AppStorage is effectively a global shared state for small data. This is convenient for authentication state, theme preference, onboarding completion flags, and feature toggles — but it is not appropriate for large data sets, sensitive data (use the Keychain instead), or complex relational data.
Code Structure
AppStorageBootcamp.swift is a minimal but complete demonstration. A single @AppStorage("name") var currentUserName: String? drives two displays: a fallback text view and a conditionally-rendered Text. A Button saves a hardcoded name "Emily" to the storage. Restart the app and the name will still be there.
Complete Code
AppStorageBootcamp.swift
import SwiftUI
struct AppStorageBootcamp: View {
@AppStorage("name") var currentUserName: String? // reads/writes UserDefaults key "name"; nil if key has never been set
var body: some View {
VStack(spacing: 20) {
Text(currentUserName ?? "Add Name Here") // nil-coalesces: shows placeholder until a name is saved
if let name = currentUserName { // only shown after the user taps Save; absent before first save
Text(name)
}
Button("Save".uppercased()) { // uppercased() transforms "Save" → "SAVE" for a bold button label
let name: String = "Emily"
currentUserName = name // writing to @AppStorage persists to UserDefaults AND triggers re-render
}
}
}
}
struct AppStorageBootcamp_Previews: PreviewProvider {
static var previews: some View {
AppStorageBootcamp()
}
}Code Walkthrough
@AppStorage("name") var currentUserName: String?— Declares a property backed byUserDefaultsunder the key"name". The type isString?(optional), so the initial value isnilwhen the key has never been set. Once a value is saved, it persists across app launches, background/foreground cycles, and device restarts (unless the user deletes the app).Text(currentUserName ?? "Add Name Here")— The nil-coalescing operator??provides a fallback string for display. Before the user taps "Save",currentUserNameis nil and the text shows "Add Name Here". After saving, it shows the stored name. This gives the UI a meaningful initial state without any conditional logic.if let name = currentUserName { Text(name) }— An optional binding that conditionally renders a second text view. This view is absent from the hierarchy whencurrentUserNameis nil, and appears after the first save. It demonstrates that@AppStoragechanges, like@Statechanges, trigger view re-evaluation.Button("Save".uppercased())—uppercased()is aStringmethod that converts "Save" to "SAVE". This is a stylistic choice — using a method call inside the button label rather than a string literal demonstrates that button labels can be any expression.currentUserName = name— Writing to an@AppStorageproperty does two things atomically: it callsUserDefaults.standard.set(name, forKey: "name")and it notifies SwiftUI to re-render the view. You don't need to callUserDefaultsdirectly at all — the property wrapper handles both sides.Persistence test — If you run this app, tap "Save", then kill and relaunch the app, "Emily" will be showing immediately —
currentUserNamereads fromUserDefaultson init, so the stored value is available before the view even appears for the first time.
Common Mistakes
Mistake: Using the same key string for different pieces of data in different views@AppStorage("name") anywhere in your app reads and writes the same UserDefaults entry. If one view stores a username under "name" and another stores a product name under the same key "name", they will overwrite each other. Use descriptive, namespaced keys like "user.profile.displayName" or define them as constants to avoid collisions.
Mistake: Storing large data (images, complex objects) in @AppStorageUserDefaults is intended for small preference values — typically a few kilobytes total. Storing large Data blobs (images, large JSON payloads) bloats the defaults database, slows down reads, and can cause performance issues on app launch (because UserDefaults loads its entire dictionary into memory at startup). Use the file system or Core Data for larger datasets.
Mistake: Storing sensitive information like passwords or tokens in @AppStorageUserDefaults is stored in a plain-text plist file that is accessible on jailbroken devices and through iTunes backups. Never store passwords, authentication tokens, credit card numbers, or any personally sensitive data in @AppStorage. Use the Keychain (KeychainServices or SecItem) for sensitive values.
Key Takeaways
@AppStorage("key")is a direct SwiftUI wrapper overUserDefaults— reading the property reads from disk, writing persists to disk and triggers a re-render automatically.- Use optional
@AppStorage(String?) to distinguish "never set" from a default value; use non-optional@AppStoragewith a default (String = "Guest") when a fallback should always be available. - Keep keys consistent and descriptive across your app — the same key string accesses the same value everywhere, which is both the power and the responsibility of using
@AppStorage.
Last updated: June 27, 2026