@AppStorage ve SwiftUI — property wrapper pro práci s UserDefaults, který automaticky synchronizuje hodnotu s UI. Při změně vlastnosti deklarované prostřednictvím @AppStorage je nová hodnota okamžitě uložena do UserDefaults, a když se UserDefaults změní zvenčí — widgetem nebo rozšířením — View se automaticky překreslí. Podle Apple Developer Documentation (2025), @AppStorage podporuje String, Int, Double, Bool, Data, URL a jejich volitelné verze, čímž poskytuje reaktivní ukládání uživatelských nastavení bez ručního kódu pozorování.
Hlavní body
@AppStorage je property wrapper představený společností Apple v iOS 14, který propojuje vlastnost View s klíčem v UserDefaults. Při čtení vlastnosti SwiftUI načte hodnotu z UserDefaults na základě zadaného klíče. Při zápisu — uloží novou hodnotu a upozorní View na nutnost překreslení.
Před příchodem @AppStorage museli vývojáři ručně číst UserDefaults v onAppear, přihlásit se k odběru oznámení UserDefaults.didChangeNotification a aktualizovat @State při změnách. @AppStorage automatizuje celý cyklus: deklarace na jednom řádku nahrazuje 15–20 řádků boilerplate kódu. Navíc @AppStorage poskytuje obousměnou synchronizaci — pokud je hodnota UserDefaults změněna z jiného procesu (např. App Extension nebo Widget), View stejně obdrží aktualizaci.
Architektonicky je @AppStorage implementován jako DynamicProperty, což umožňuje SwiftUI sledovat závislosti a překreslit View při změně pozorované hodnoty. To jej činí ideálním pro ukládání uživatelských nastavení: jazyk rozhraní, zapínání/vypínání funkcí, naposledy vybraná záložka, uživatelské jméno.
Přestože @AppStorage pod kapotou používá UserDefaults, přístupy k práci s úložištěm se zásadně liší. UserDefaults je nízkoúrovňové API vyžadující ruční správu čtení, zápisu a oznámení o změnách. @AppStorage je SwiftUI abstrakce poskytující reaktivní chování ihned po vyjmutí z krabice.
UserDefaults je vhodný pro jednorázové operace: načítání nastavení při startu aplikace, zápis analytiky, ukládání tokenů do mezipaměti. @AppStorage — pro nastavení, která by měla reaktivně aktualizovat UI: přepínače motivů, výběr jazyka, ukládání stavu rozhraní. Přímé použití UserDefaults uvnitř View je antivzor, protože View se o změnách nedozví bez dalšího přihlášení k odběru.
| Parametr | @AppStorage | UserDefaults |
|---|---|---|
| Reaktivita | Automatická | Vyžaduje přihlášení k oznámením |
| Boilerplate | 1 řádek na vlastnost | 15–20 řádků na vlastnost |
| Typy | String, Int, Double, Bool, Data, URL | Všechny typy + archivované objekty |
| Vlastní typy | Přes RawRepresentable | Přes NSKeyedArchiver |
| App Extension | Automatická synchronizace | Ruční přihlášení |
Pro jednoduchá nastavení s reaktivním UI je @AppStorage preferovanou volbou. Pro složitá data (pole, slovníky, vlastní objekty) použijte kombinaci UserDefaults s @State a ručním přihlášením ke změnám, nebo přejděte na SwiftData / Core Data pro strukturované ukládání.
@AppStorage podporuje standardní typy, které UserDefaults může přímo serializovat: String, Int, Double, Bool, Data, URL. Pro každý typ existuje volitelná verze (String?, Int?, Double?, Bool?, Data?, URL?), která umožňuje rozlišení mezi „nenastaveno” a „prázdná hodnota”.
Pro ukládání vlastních typů vyhovujících protokolu RawRepresentable funguje @AppStorage také automaticky. Pokud má enum rawValue typu String nebo Int, lze jej použít přímo: @AppStorage("theme") var theme: AppTheme = .system. SwiftUI automaticky serializuje/deserializuje hodnotu prostřednictvím rawValue.
enum AppTheme: String {
case system, light, dark
}
struct SettingsView: View {
@AppStorage("username") var username: String = "Guest"
@AppStorage("launchCount") var launchCount: Int = 0
@AppStorage("isDarkMode") var isDarkMode: Bool = false
@AppStorage("appTheme") var theme: AppTheme = .system
@AppStorage("lastOpened") var lastOpened: Date? = nil
var body: some View {
Form {
TextField("Username", text: $username)
Toggle("Dark mode", isOn: $isDarkMode)
Text("Spuštěno \(launchCount) krát")
}
}
}
V příkladu jsou použity různé typy @AppStorage: String s výchozí hodnotou „Guest”, Int pro poĈítadlo spuštění, Bool pro tmavý motiv, enum AppTheme s rawValue typu String a volitelný Date? pro čas posledního otevření. Každá vlastnost je vázána na klíč UserDefaults uvedený jako první argument. Výchozí hodnota se použije, pokud klíč v úložišti při prvním spuštění chybí.
Jednou z klíčových výhod @AppStorage — automatické pozorování změn UserDefaults z libovolného zdroje. Pokud App Extension nebo Widget změní hodnotu, @AppStorage v rodičovské aplikaci obdrží oznámení a překreslí View. Toho je dosaženo prostřednictvím mechanismu KVO (Key-Value Observing), který @AppStorage automaticky nastaví na UserDefaults.didChangeNotification.
V praxi to znamená, že pokud uživatel změní nastavení ve Widgetu (např. zapne tmavý motiv), aplikace tuto změnu okamžitě převezme. Podobně funguje synchronizace mezi hlavní aplikací a Share Extension, Watch App nebo Today Widget. Vývojář nemusí psát kód pro meziprocesovou výměnu dat — @AppStorage to dělá automaticky.
struct ThemeSettingView: View {
@AppStorage("isDarkMode") var isDarkMode: Bool = false
var body: some View {
VStack {
Toggle("Dark Mode", isOn: $isDarkMode)
.onChange(of: isDarkMode) { oldValue, newValue in
print("Tmavý režim změněn na \(newValue)")
}
}
}
}
Toggle je propojen s $isDarkMode prostřednictvím @AppStorage. Při přepnutí je hodnota automaticky uložena do UserDefaults pod klíčem „isDarkMode”. Modifikátor .onChange umožňuje provést vedlejší akci při změně — například odeslat analytiku nebo aktualizovat UI jiných obrazovek. Pokud Widget změní stejný klíč, @AppStorage také zavolá onChange, čímž zajistí konzistenci stavu.
Podívejme se na úplnou obrazovku nastavení aplikace, která používá @AppStorage pro ukládání všech konfigurací. Formulář obsahuje sekce s různými typy nastavení: textová pole, přepínače, počítadla — všechny hodnoty jsou automaticky ukládány do UserDefaults.
struct AppSettingsView: View {
@AppStorage("displayName") var displayName = ""
@AppStorage("notificationsEnabled") var notificationsEnabled = true
@AppStorage("maxResults") var maxResults = 25
@AppStorage("selectedTab") var selectedTab = "home"
var body: some View {
NavigationStack {
Form {
Section(header: Text("Profil")) {
TextField("Display name", text: $displayName)
}
Section(header: Text("Předvolby")) {
Toggle("Enable notifications",
isOn: $notificationsEnabled)
Stepper("Max results: \(maxResults)",
value: $maxResults,
in: 10...100,
step: 5)
}
Section {
Button("Resetovat nastavení") {
UserDefaults.standard.removePersistentDomain(
forName: Bundle.main.bundleIdentifier!)
}
.tint(.red)
}
}
.navigationTitle("Settings")
}
}
}
Formulář obsahuje čtyři vlastnosti @AppStorage různých typů: String pro jméno, Bool pro oznámení, Int pro počet výsledků a String pro vybranou záložku. Všechny ovládací prvky jsou propojeny s vlastnostmi prostřednictvím Binding ($displayName, $notificationsEnabled atd.). Tlačítko „Reset settings” resetuje všechna UserDefaults, odstraní doménu aplikace — poté se @AppStorage automaticky vrátí k výchozím hodnotám.
struct SharedSettingsView: View {
let sharedDefaults = UserDefaults(suiteName: "group.com.example.app")
@AppStorage("widgetTheme", store: UserDefaults(suiteName: "group.com.example.app")!)
var widgetTheme: String = "systém"
@AppStorage("widgetColor", store: UserDefaults(suiteName: "group.com.example.app")!)
var widgetColor: String = "modrá"
var body: some View {
Form {
Picker("Widget theme", selection: $widgetTheme) {
Text("Systém").tag("system")
Text("Světlý").tag("světlý")
Text("Tmavý").tag("tmavý")
}
Picker("Accent color", selection: $widgetColor) {
Text("Modrá").tag("blue")
Text("Zelená").tag("zelená")
Text("Červená").tag("červená")
}
}
}
}
Pro App Group (sdílené úložiště mezi aplikací a rozšířeními) @AppStorage přijímá parametr store: UserDefaults(suiteName:). Hodnoty jsou ukládány do sdíleného kontejneru přístupného hlavní aplikaci, Widgetu, Watch App a dalším rozšířením stejné skupiny. Widget může číst tato nastavení a při změně v aplikaci se Widget automaticky aktualizuje prostřednictvím mechanismu pozorování UserDefaults.
Často kladené otázky
@State ukládá hodnotu pouze v paměti a resetuje se při restartu aplikace. @AppStorage ukládá hodnotu do UserDefaults a obnoví ji při dalším spuštění. Použijte @State pro dočasná data obrazovky, @AppStorage — pro nastavení, která mají přežít restart.
Ano, pokud Enum implementuje protokol RawRepresentable s rawValue typu String nebo Int. Příklad: @AppStorage("theme") var theme: AppTheme = .system. SwiftUI automaticky serializuje enum prostřednictvím rawValue a obnoví jej při načtení.
Zavolejte UserDefaults.standard.removePersistentDomain(forName: Bundle.main.bundleIdentifier!) pro standardní úložiště nebo removeObject(forKey:) pro konkrétní klíč. Po vymazání se všechny vlastnosti @AppStorage vrátí k výchozím hodnotám uvedeným v deklaraci.
Ano, pro synchronizaci mezi aplikací a rozšířeními použijte App Group: @AppStorage("key", store: UserDefaults(suiteName: "group.com.example.app")!). Widget, Share Extension a Watch App mohou číst a zapisovat do stejného UserDefaults a změny jsou automaticky sledovány.
@AppStorage používá UserDefaults, který je určen pro malá množství dat: nastavení, tokeny, počítadla. Doporučený limit je do 100 KB na aplikaci. Pro strukturovaná nebo velká data (pole objektů, mediální soubory) použijte SwiftData, Core Data nebo souborový systém.
Shrnutí
Vyvineme mobilní aplikaci na klíč
IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.
Přečtěte si také