@AppStorage in SwiftUI è un property wrapper per lavorare con UserDefaults che sincronizza automaticamente il valore con l'interfaccia utente. Quando una proprietà dichiarata con @AppStorage cambia, il nuovo valore viene immediatamente salvato in UserDefaults, e quando UserDefaults cambia dall'esterno — da un widget o estensione — la View viene automaticamente ridisegnata. Secondo Apple Developer Documentation (2025), @AppStorage supporta String, Int, Double, Bool, Data, URL e le loro versioni opzionali, fornendo archiviazione reattiva delle impostazioni utente senza codice di osservazione manuale.
Punti Chiave
@AppStorage è un property wrapper introdotto da Apple in iOS 14 che lega una proprietà della View a una chiave in UserDefaults. Durante la lettura della proprietà, SwiftUI carica il valore da UserDefaults tramite la chiave specificata. Durante la scrittura, salva il nuovo valore e notifica alla View che deve essere ridisegnata.
Prima di @AppStorage, gli sviluppatori dovevano leggere manualmente UserDefaults in onAppear, iscriversi a UserDefaults.didChangeNotification e aggiornare @State ai cambiamenti. @AppStorage automatizza l'intero ciclo: una dichiarazione di una riga sostituisce 15–20 righe di codice boilerplate. Inoltre, @AppStorage fornisce sincronizzazione bidirezionale — se il valore di UserDefaults cambia da un altro processo (es. App Extension o Widget), la View riceve comunque l'aggiornamento.
Architetturalmente, @AppStorage è implementato come DynamicProperty, che consente a SwiftUI di tracciare le dipendenze e ridisegnare la View quando il valore osservato cambia. Questo lo rende ideale per memorizzare le impostazioni utente: lingua dell'interfaccia, abilitazione/disabilitazione delle funzionalità, ultimo tab selezionato, nome utente.
Sebbene @AppStorage utilizzi UserDefaults internamente, gli approcci al lavoro con l'archiviazione sono fondamentalmente diversi. UserDefaults è un'API di basso livello che richiede la gestione manuale di lettura, scrittura e notifiche di modifica. @AppStorage è un'astrazione di SwiftUI che fornisce un comportamento reattivo immediato.
UserDefaults è adatto per operazioni una tantum: caricare impostazioni all'avvio dell'app, scrivere analisi, memorizzare nella cache token. @AppStorage è per impostazioni che devono aggiornare l'UI in modo reattivo: interruttori del tema, selezione della lingua, salvare lo stato dell'interfaccia. Usare UserDefaults direttamente all'interno di una View è un anti-pattern, poiché la View non viene a conoscenza delle modifiche senza abbonamento aggiuntivo.
| Parametro | @AppStorage | UserDefaults |
|---|---|---|
| Reattività | Automatica | Richiede abbonamento alle notifiche |
| Boilerplate | 1 riga per proprietà | 15–20 righe per proprietà |
| Tipi | String, Int, Double, Bool, Data, URL | Tutti i tipi + oggetti archiviati |
| Tipi personalizzati | Tramite RawRepresentable | Tramite NSKeyedArchiver |
| App Extension | Sincronizzazione automatica | Abbonamento manuale |
Per impostazioni semplici con UI reattiva @AppStorage è la scelta preferita. Per dati complessi (array, dizionari, oggetti personalizzati) utilizzare una combinazione di UserDefaults con @State e abbonamento manuale alle modifiche, o passare a SwiftData / Core Data per l'archiviazione strutturata.
@AppStorage supporta i tipi standard che UserDefaults può serializzare direttamente: String, Int, Double, Bool, Data, URL. Per ogni tipo esiste una versione opzionale (String?, Int?, Double?, Bool?, Data?, URL?) che consente di distinguere tra "non impostato" e "valore vuoto".
Per memorizzare tipi personalizzati conformi al protocollo RawRepresentable, @AppStorage funziona anche automaticamente. Se un enum ha rawValue di tipo String o Int, può essere usato direttamente: @AppStorage("theme") var theme: AppTheme = .system. SwiftUI serializza/deserializza automaticamente il valore tramite 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("Avviata \(launchCount) volte")
}
}
}
L'esempio utilizza diversi tipi di @AppStorage: String con valore predefinito "Guest", Int per un contatore di avvii, Bool per il tema scuro, enum AppTheme con rawValue di tipo String e un Date? opzionale per l'ultima ora di apertura. Ogni proprietà è legata a una chiave UserDefaults specificata come primo argomento. Il valore predefinito viene utilizzato se la chiave non è presente nell'archivio al primo avvio.
Uno dei principali vantaggi di @AppStorage è l'osservazione automatica delle modifiche di UserDefaults da qualsiasi fonte. Se un'App Extension o un Widget modifica un valore, @AppStorage nell'app principale riceve la notifica e ridisegna la View. Ciò è ottenuto tramite il meccanismo KVO (Key-Value Observing) che @AppStorage configura automaticamente su UserDefaults.didChangeNotification.
In pratica, ciò significa che se l'utente modifica un'impostazione in un Widget (ad esempio, attiva il tema scuro), l'app cattura immediatamente la modifica. La stessa sincronizzazione funziona tra l'app principale e Share Extension, Watch App o Today Widget. Lo sviluppatore non deve scrivere codice per lo scambio di dati tra processi — @AppStorage lo fa automaticamente.
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("Modalità scura cambiata a \(newValue)")
}
}
}
}
Toggle è legato a $isDarkMode tramite @AppStorage. Quando si attiva, il valore viene automaticamente salvato in UserDefaults sotto la chiave "isDarkMode". Il modificatore .onChange consente di eseguire un effetto collaterale al cambiamento — ad esempio, inviare analisi o aggiornare l'UI di altre schermate. Se un Widget modifica la stessa chiave, @AppStorage attiverà anche onChange, garantendo la coerenza dello stato.
Consideriamo una schermata completa delle impostazioni dell'app che utilizza @AppStorage per memorizzare tutte le configurazioni. Il modulo contiene sezioni con diversi tipi di impostazioni: campi di testo, interruttori, contatori — tutti i valori vengono automaticamente salvati in 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("Profilo")) {
TextField("Display name", text: $displayName)
}
Section(header: Text("Preferenze")) {
Toggle("Enable notifications",
isOn: $notificationsEnabled)
Stepper("Max results: \(maxResults)",
value: $maxResults,
in: 10...100,
step: 5)
}
Section {
Button("Reimposta impostazioni") {
UserDefaults.standard.removePersistentDomain(
forName: Bundle.main.bundleIdentifier!)
}
.tint(.red)
}
}
.navigationTitle("Settings")
}
}
}
Il modulo contiene quattro proprietà @AppStorage di diversi tipi: String per il nome, Bool per le notifiche, Int per il numero di risultati e String per il tab selezionato. Tutti i controlli sono legati alle proprietà tramite Binding ($displayName, $notificationsEnabled, ecc.). Il pulsante "Reset settings" cancella tutti gli UserDefaults rimuovendo il dominio dell'app — dopodiché @AppStorage torna automaticamente ai valori predefiniti.
struct SharedSettingsView: View {
let sharedDefaults = UserDefaults(suiteName: "group.com.example.app")
@AppStorage("widgetTheme", store: UserDefaults(suiteName: "group.com.example.app")!)
var widgetTheme: String = "sistema"
@AppStorage("widgetColor", store: UserDefaults(suiteName: "group.com.example.app")!)
var widgetColor: String = "blu"
var body: some View {
Form {
Picker("Widget theme", selection: $widgetTheme) {
Text("Sistema").tag("system")
Text("Chiaro").tag("chiaro")
Text("Scuro").tag("scuro")
}
Picker("Accent color", selection: $widgetColor) {
Text("Blu").tag("blue")
Text("Verde").tag("verde")
Text("Rosso").tag("rosso")
}
}
}
}
Per App Group (archiviazione condivisa tra l'app e le estensioni) @AppStorage accetta il parametro store: UserDefaults(suiteName:). I valori vengono salvati nel contenitore condiviso disponibile per l'app principale, Widget, Watch App e altre estensioni dello stesso gruppo. Un Widget può leggere queste impostazioni e quando cambiano nell'app, il Widget si aggiorna automaticamente tramite il meccanismo di osservazione di UserDefaults.
Domande Frequenti
@State memorizza il valore solo in memoria e viene resettato al riavvio dell'app. @AppStorage salva il valore in UserDefaults e lo ripristina al successivo avvio. Usa @State per dati temporanei dello schermo, @AppStorage per impostazioni che devono sopravvivere al riavvio.
Sì, se l'Enum implementa il protocollo RawRepresentable con rawValue di tipo String o Int. Esempio: @AppStorage("theme") var theme: AppTheme = .system. SwiftUI serializza automaticamente l'enum tramite rawValue e lo ripristina al caricamento.
Chiama UserDefaults.standard.removePersistentDomain(forName: Bundle.main.bundleIdentifier!) per l'archivio standard o removeObject(forKey:) per una chiave specifica. Dopo la cancellazione, tutte le proprietà @AppStorage torneranno ai valori predefiniti specificati nella dichiarazione.
Sì, per la sincronizzazione tra l'app e le estensioni usa App Group: @AppStorage("key", store: UserDefaults(suiteName: "group.com.example.app")!). Widget, Share Extension e Watch App possono leggere e scrivere nello stesso UserDefaults, e le modifiche vengono tracciate automaticamente.
@AppStorage usa UserDefaults, progettato per piccole quantità di dati: impostazioni, token, contatori. Il limite raccomandato è fino a 100 KB per app. Per dati strutturati o grandi (array di oggetti, file multimediali) usa SwiftData, Core Data o il file system.
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche