La proprietà body è l'elemento centrale del protocollo View in SwiftUI, determinando quale contenuto viene visualizzato sullo schermo. Secondo Apple Developer Documentation, 2024, body è l'unico requisito obbligatorio del protocollo View e restituisce un tipo che è conforme allo stesso protocollo. SwiftUI chiama body ad ogni cambiamento di stato per costruire e confrontare un nuovo albero di elementi.
Punti Chiave
body è una proprietà calcolata che è l'unico requisito obbligatorio del protocollo View. Ogni struttura conforme a View deve implementare body. La proprietà restituisce il contenuto che SwiftUI visualizza sullo schermo — può essere testo, un'immagine, un pulsante, un contenitore con elementi annidati o qualsiasi altro tipo conforme al protocollo View.
La firma di body è sempre fissa: var body: some View { get }. Il tipo di ritorno è some View (un tipo opaco), non un tipo concreto. Ciò significa che diverse Views possono restituire diversi tipi concreti in body, ma il compilatore Swift fissa il tipo concreto per ogni implementazione al momento della compilazione.
Secondo WWDC 2022, body è il punto di ingresso per la descrizione dichiarativa dell'interfaccia. A differenza di UIKit, dove crei e configuri UIView in modo imperativo, in SwiftUI descrivi in modo dichiarativo cosa deve essere visualizzato e SwiftUI stesso calcola come implementarlo.
body dovrebbe comportarsi come una funzione pura — con gli stessi input (proprietà della struttura e stato) dovrebbe restituire lo stesso albero View. Se body dipende da uno stato mutabile esterno (variabili globali, UserDefaults senza il wrapper @AppStorage), il comportamento diventa imprevedibile e SwiftUI potrebbe ridisegnare lo schermo in modo errato.
La proprietà calcolata body non memorizza un valore — viene calcolata ogni volta che viene acceduta. Quando SwiftUI determina che lo stato è cambiato, ricrea la struttura View e legge il nuovo valore di body per ottenere l'albero corrente di elementi da visualizzare.
struct CounterView: View {
@State private var count = 0
var body: some View {
VStack {
Text("Contatore: \(count)")
.font(.largeTitle)
Button("Incrementa") {
count += 1
}
.padding()
.background(.blue)
.foregroundColor(.white)
.cornerRadius(8)
}
}
}
In questo esempio, body restituisce un VStack contenente un Text e un pulsante con modificatori. Quando il pulsante viene premuto, la proprietà @State count viene incrementata, SwiftUI ricrea la struttura CounterView e chiama di nuovo body per ottenere l'albero aggiornato con il nuovo valore di Text.
I modificatori (.font, .padding, .background, .foregroundColor, .cornerRadius) non modificano la View originale ma la avvolgono in ModifiedContent — un nuovo tipo che aggiunge la modifica. Ogni modificatore crea un altro livello di annidamento, cosa importante da considerare per le prestazioni.
some View nel tipo di ritorno di body non è solo una convenzione ma un requisito del compilatore. Swift richiede che tutti i percorsi di ritorno in body abbiano lo stesso tipo concreto. Senza @ViewBuilder non puoi restituire Text in un ramo e Button in un altro — il compilatore produrrà un errore.
struct ConditionalView: View {
var isReady: Bool
@ViewBuilder
var body: some View {
if isReady {
Text("Pronto")
.foregroundColor(.green)
} else {
ProgressView()
}
}
}
@ViewBuilder su body permette di usare logica condizionale (if/else, switch) senza errori di compilazione. ViewBuilder avvolge automaticamente diversi rami in ConditionalContent — un tipo speciale che nasconde le differenze dei tipi concreti. Questa è una capacità chiave per costruire interfacce dinamiche.
Senza @ViewBuilder, il compilatore tenta di inferire un unico tipo per tutti i percorsi di ritorno. Se i tipi differiscono — si verifica un errore. Questo è il motivo per cui SwiftUI applica implicitamente @ViewBuilder a body nelle dichiarazioni View, sebbene nel codice utente sia necessario aggiungere esplicitamente l'annotazione per metodi e proprietà personalizzati che restituiscono più Views.
Usare some View invece di un tipo concreto non riduce le prestazioni — il compilatore conosce il tipo esatto al momento della compilazione e genera codice diretto senza dispatch dinamico. AnyView, al contrario, usa l'eliminazione del tipo (type erasure) con il sovraccarico di avvolgimento in un contenitore esistenziale.
body viene chiamata da SwiftUI in tre scenari principali: quando la View viene visualizzata per la prima volta, quando @State/@Binding/@ObservedObject/@StateObject cambia, e quando la View genitore passa nuovi valori attraverso l'inizializzatore. SwiftUI può anche chiamare body quando i valori dell'ambiente (@Environment) cambiano.
La frequenza delle chiamate a body non dovrebbe preoccuparti — SwiftUI ottimizza il ridisegno attraverso il meccanismo di identità. Ogni View nella gerarchia ha un identificatore univoco. Se l'identità e i dati di input non sono cambiati — body non viene chiamata anche se la View genitore viene ridisegnata. Ciò viene ottenuto attraverso il confronto Equatable e la stabilità strutturale.
struct ParentView: View {
var body: some View {
ChildView(name: "Alice") // Identità stabile
}
}
struct ChildView: View {
let name: String
var body: some View {
Text("Ciao, \(name)!")
}
}
In questo esempio, se ParentView viene ridisegnata ma passa lo stesso valore name — ChildView.body non viene chiamata. SwiftUI confronta i dati di input della struttura e, se invariati, salta il ridisegno del componente figlio. Questo è il meccanismo di differenziazione delle viste.
Esistono diverse trappole che portano a chiamate inaspettate di body: uso di classi senza ObservableObject, passaggio di chiusure create all'interno di body (ogni creazione di chiusura genera una nuova identità) e uso errato di EquatableView. Se body viene chiamata troppo spesso — verifica la stabilità dell'identità di tutti i componenti figli.
Prima regola: body dovrebbe essere minimale. Sposta la logica complessa in proprietà calcolate o metodi separati che restituiscono View. Ciò migliora la leggibilità e permette a SwiftUI di determinare con maggiore precisione quali parti della gerarchia sono cambiate. Dividi i body grandi in sottocomponenti con chiari limiti di responsabilità.
Seconda regola: non usare body per eseguire lavoro. Caricamento dati, operazioni di rete, scritture su database — tutto questo dovrebbe avvenire al di fuori di body, in attività (tasks), modificatori onChange o attraverso ObservableObject. body è destinato esclusivamente alla dichiarazione dell'interfaccia.
Terza regola: usa la proprietà EquatableView o un protocollo Equatable personalizzato per Views se il confronto strutturale standard è insufficiente. Ciò consente di indicare esplicitamente a SwiftUI quando una View figlia necessita di un ridisegno ed evitare chiamate non necessarie a body.
Quarta regola: se body contiene calcoli complessi (formattazione, filtraggio, ordinamento) — usa @State per la memorizzazione nella cache del risultato o sposta i calcoli in un metodo separato chiamato da onChange. Calcoli ripetuti in body ad ogni aggiornamento di stato sono una causa comune di rallentamento delle animazioni.
Quinta regola: per gli elenchi (List, ForEach) assicura identificatori stabili tramite il parametro id. Senza un'identità stabile, ForEach ricrea tutti gli elementi ad ogni modifica, chiamando body per ciascuno di essi, anche se solo un elemento è cambiato.
Domande Frequenti
body è la proprietà calcolata del protocollo View che restituisce il contenuto da visualizzare. È l'unico requisito obbligatorio del protocollo. Il tipo di ritorno è some View, che consente a SwiftUI di ottimizzare la gerarchia al momento della compilazione.
Sì, SwiftUI chiama body ad ogni cambiamento di stato (@State, @Binding, @ObservedObject) o di dati di input. Questo è un comportamento normale per un framework dichiarativo. SwiftUI ottimizza la frequenza delle chiamate attraverso il meccanismo di identità e il confronto Equatable.
some View è un tipo opaco che nasconde l'implementazione concreta. Il compilatore fissa il tipo al momento della compilazione, garantendo prestazioni di chiamata diretta. Ciò offre flessibilità: puoi cambiare il tipo di ritorno senza modificare la firma.
No, body non può essere opzionale — il tipo di ritorno some View non consente nil. Se devi nascondere un elemento condizionalmente, usa la logica condizionale all'interno di @ViewBuilder o restituisci EmptyView, che non occupa spazio nella gerarchia.
Ogni modificatore crea un nuovo livello ModifiedContent, aumentando la profondità della gerarchia. Per la maggior parte degli schermi (fino a 50 modificatori) l'impatto è trascurabile. Un numero eccessivo di modificatori (centinaia) può rallentare il diffing. Raggruppa i modificatori correlati in estensioni personalizzate.
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