How does @AppStorage behave: where is the value, what happens when it is removed or changed elsewhere, and what can you store?
Predict first
What does the view render as the stored value changes?
UserDefaults.standard.removeObject(forKey: "k")
struct V: View {
@AppStorage("k") var n = 3
var body: some View { let _ = note("body n=\(n)"); Text("\(n)") }
}
_ = show(V()); flush()
UserDefaults.standard.set(5, forKey: "k"); spin(); flush()
UserDefaults.standard.removeObject(forKey: "k"); spin(); flush()
print("stored value is now", UserDefaults.standard.object(forKey: "k") as Any)
- 3, then 5 after an external set, then 3 after removal, nothing stored
- 3, then 3, then 3
- 5 from the start
Mental model. @AppStorage is a wall-mounted settings board that every view reads from: the initial value is the printed default on the board, not something written on it.
@AppStorage("key") var n = 3 reads and writes UserDefaults (the standard suite unless you pass store:). The declared value is only a default: it is not written until you set something. In the check the view first shows 3 with nothing stored, a UserDefaults.standard.set(5, …) from outside any view re-renders it with 5 (it observes the defaults key, so changes from code, other views or an extension sharing the suite all arrive), and removing the key brings back the default 3 and leaves nothing stored (nil). Several views using the same key stay in sync for the same reason.
Limits to remember: supported types are the property-list ones plus URL, Data, RawRepresentable enums with an Int or String raw value, and optionals of those; anything else (a Codable struct) needs encoding yourself. Defaults is for small preferences, not caches or secrets (use the Keychain), and it is not transactional: two writers race. @SceneStorage is the per-scene sibling for UI state such as a selected tab or draft text; the system may discard it, so it restores convenience state and never data you cannot lose. Changing the key string later silently starts from the default, so give keys constants.
@AppStorage observes UserDefaults and falls back to its default.
Common traps
- Treating the initial value as stored. It is a fallback until you write.
- Putting large or secret data in it. Defaults is for small preferences.
They might follow up with
- How do you share it with a widget? An app-group suite via
store:. - How do you store a struct? Encode it to
Dataor a string raw value yourself.
Related questions
- 8.4.2 What is the App protocol, what are scenes, and what does scenePhase tell you?
- 8.10.5 If a parent changes one environment value, which child views re-run their body?
- 8.3.1 What do @State, @Binding and @Bindable do, and who owns the data?
← If a parent changes one environment value, which child views re-run…