@State è un Property Wrapper in SwiftUI per gestire lo stato locale all'interno di una singola vista. SwiftUI ridisegna automaticamente la vista ogni volta che una proprietà @State cambia, rendendo l'interfaccia reattiva senza chiamate di aggiornamento manuali. Secondo la Apple Developer Documentation (2025), @State è consigliato per tipi semplici e strutture appartenenti a una singola vista. @State è il modo più semplice per aggiungere interattività a un'interfaccia SwiftUI.
Punti chiave
@State è un Property Wrapper integrato in SwiftUI che consente a una vista di memorizzare e tracciare il proprio stato. Quando un valore @State cambia, SwiftUI ridisegna automaticamente la vista reinvocando la proprietà body. Questa è la base della programmazione reattiva in SwiftUI: lo sviluppatore dichiara lo stato e il framework si occupa della sincronizzazione dell'interfaccia.
@State crea un'area di archiviazione nell'heap gestita da SwiftUI. Quest'area è persistente — sopravvive alle inizializzazioni ripetute della struttura della vista, che avvengono a ogni rendering. SwiftUI utilizza l'identificatore della vista (generato dalla sua posizione nella gerarchia) per legare la proprietà @State a una vista specifica. Grazie a ciò, lo stato non viene resettato quando la vista padre viene aggiornata.
Un limite importante: @State è destinato solo a tipi valore (struct, enum, primitivi). Per i tipi riferimento (classi), usa @StateObject o @ObservedObject. Se assegni una classe a una proprietà @State, SwiftUI non sarà in grado di rilevare le modifiche all'interno dell'oggetto — solo la sostituzione completa del riferimento.
SwiftUI implementa @State attraverso un meccanismo interno di Storage. Ogni proprietà @State ottiene una cella di memoria dedicata memorizzata in un contenitore di archiviazione speciale della vista. Quando si verifica una scrittura in wrappedValue, SwiftUI notifica il suo grafo delle dipendenze tramite didSet della necessità di ridisegnare.
struct ContentView: View {
@State private var name: String = "User"
@State private var isLoggedIn: Bool = false
var body: some View {
VStack {
Text("Ciao, \(name)")
Button(isLoggedIn ? "Esci" : "Accedi") {
isLoggedIn.toggle()
}
}
}
}
Nell'esempio, ci sono due proprietà @State: name (String) e isLoggedIn (Bool). Quando viene chiamato isLoggedIn.toggle(), SwiftUI segna ContentView come bisognosa di aggiornamento e reinvoca body nel ciclo di rendering successivo. Il punto chiave: le proprietà @State sono sempre dichiarate con il modificatore private — questo segnala che lo stato appartiene esclusivamente alla vista corrente e non dovrebbe essere modificato dall'esterno direttamente.
Per osservare le modifiche, SwiftUI utilizza CurrentValueSubject da Combine. Ogni proprietà @State crea un publisher nascosto che notifica il sistema a ogni cambiamento. Ciò consente a SwiftUI di ridisegnare solo il set minimo necessario di viste, evitando aggiornamenti completi della gerarchia.
@State è ottimale per stati locali semplici: campi di testo nella ricerca, flag booleani per finestre modali, interruttori di impostazioni, contatori, elementi di lista selezionati. Se un valore viene utilizzato solo in una vista e nei suoi componenti figli (tramite @Binding), @State è la scelta giusta. Per stati che devono sopravvivere alla chiusura della vista (ad esempio, dati del modulo), @State funziona anche finché la vista rimane nella gerarchia.
Non usare @State per stati globali dell'applicazione, memorizzazione nella cache di dati di rete o oggetti utilizzati su più schermate. @StateObject e @EnvironmentObject sono progettati per questi scopi. Inoltre, @State non è adatto per memorizzare grandi quantità di dati — ogni modifica ridisegnerà l'intera vista.
@Binding è un ponte tra @State in una vista padre e una vista figlia che deve modificare quello stato. Il padre dichiara @State, e il componente figlio riceve un Binding tramite la proiezione $. La modifica del Binding nella vista figlia aggiorna automaticamente @State nel padre — e viceversa. Ciò garantisce un flusso di dati unidirezionale con capacità di feedback.
struct ParentView: View {
@State private var text: String = ""
var body: some View {
ChildView(text: $text)
}
}
struct ChildView: View {
@Binding var text: String
var body: some View {
TextField("Enter text", text: $text)
}
}
Nel listato, ParentView possiede @State text, e ChildView riceve $text come Binding. Il TextField all'interno di ChildView si lega a questo Binding tramite text: $text. Quando l'utente digita nel TextField, il valore cambia in ChildView attraverso il Binding, causando un aggiornamento di @State in ParentView. Entrambe le viste vengono ridisegnate con il nuovo valore.
L'errore più comune è assegnare una classe a una proprietà @State. Se scrivi @State var model = MyClass(), SwiftUI non sarà in grado di tracciare le modifiche alle proprietà all'interno della classe — solo la sostituzione dell'oggetto. Per le classi, usa sempre @StateObject. Il secondo problema comune è dichiarare @State senza il modificatore private, violando il principio di incapsulamento dello stato.
Passare @State direttamente a una vista figlia senza $ è un altro errore tipico. Se passi TextField(text: text) invece di TextField(text: $text), il componente figlio riceve solo una stringa, non un Binding. Le modifiche al testo nel TextField non verranno sincronizzate con @State del padre. Usa sempre la proiezione $ per passare Binding.
Il terzo errore sono molteplici proprietà @State per dati correlati. Se più valori formano logicamente un tutt'uno (ad esempio, campi del modulo), combinali in un'unica struttura con un singolo @State. Questo semplifica il passaggio dello stato alle viste figlie e riduce il numero di trigger di aggiornamento individuali.
@State viene utilizzato nella maggior parte dei progetti SwiftUI per l'interattività di base. Consideriamo un esempio di modulo di accesso in cui @State gestisce i campi di testo e lo stato di caricamento. Questo pattern appare in ogni applicazione — dalle semplici note alle complesse soluzioni aziendali.
struct LoginView: View {
@State private var email: String = ""
@State private var password: String = ""
@State private var isLoading: Bool = false
@State private var errorMessage: String?
var body: some View {
Form {
TextField("Email", text: $email)
SecureField("Password", text: $password)
Button("Accedi") {
login()
}.disabled(isLoading)
}
}
private func login() {
isLoading = true
// Esegui richiesta di rete
}
}
Nell'esempio, ci sono quattro proprietà @State: email e password per i campi del modulo, isLoading per l'indicazione di caricamento e errorMessage per la visualizzazione degli errori. Ogni proprietà gestisce indipendentemente la sua parte dell'interfaccia. Quando isLoading cambia, il pulsante viene automaticamente disabilitato tramite disabled(isLoading) — senza aggiornamento manuale dell'interfaccia.
Domande frequenti
@State è progettato per lo stato locale di una vista specifica. Il modificatore private garantisce che altri componenti non possano modificarlo direttamente, rompendo l'incapsulamento. Per l'accesso esterno, usa la proiezione $.
Sì, @State supporta array e dizionari poiché sono tipi valore. Tuttavia, quando un elemento di un array cambia, SwiftUI ridisegna l'intera vista. Per liste grandi, @StateObject con @Published è più efficiente.
@State funziona correttamente con i tipi Optional. Quando nil viene assegnato, SwiftUI rileva la modifica e ridisegna la vista. Questo è comodo per stati come errorMessage: String?, dove nil significa nessun errore.
@State mantiene il valore finché la vista rimane nella gerarchia. Se la vista viene rimossa dalla gerarchia e aggiunta di nuovo, @State viene reinizializzato con il valore predefinito. Per la persistenza, usa @AppStorage.
Sì, racchiudi la modifica in withAnimation: withAnimation(.easeInOut) { isExpanded.toggle() }. SwiftUI anima la transizione tra il vecchio e il nuovo stato dell'interfaccia con il tipo di animazione specificato.
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