Result Builder è un attributo Swift implementato tramite il protocollo @resultBuilder che trasforma una sequenza di espressioni in un valore composto. Il compilatore converte blocchi di codice con costrutti di controllo if, for, switch in chiamate a metodi statici del builder — buildBlock, buildEither, buildArray. Secondo la proposta Swift Evolution SE-0289 (2022), i result builders consentono di creare DSL dichiarativi all'interno di Swift senza parser esterni. L'esempio più noto è @ViewBuilder in SwiftUI, dove il corpo della view è costruito da elementi condizionali e ciclici in uno stile dichiarativo.
Punti chiave
TupleViewif/else, switch, for-in tramite i metodi buildOptional, buildEither, buildArrayResult Builder (precedentemente noto come function builders) è un meccanismo Swift che consente di trasformare una sequenza di espressioni separate da interruzioni di riga in un singolo valore. Viene dichiarato utilizzando l'attributo @resultBuilder applicato a una struttura che implementa metodi di trasformazione statici.
Prima dei result builders, la sintassi dichiarativa del body di SwiftUI era impossibile. Invece di un elenco compatto di viste, gli sviluppatori dovevano scrivere manualmente chiamate TupleView. Result Builder avvolge automaticamente ogni espressione, supporta ramificazioni e cicli, nascondendo allo sviluppatore la complessità della composizione.
Secondo Swift Evolution SE-0289, accettata nel 2022, il result builder è un'evoluzione dell'idea dei function builders (SE-0258, Swift 5.1). Modifiche principali: rinomina da @_functionBuilder a @resultBuilder ed estensione ai parametri di funzione, consentendo di utilizzare i builder per qualsiasi argomento closure, non solo per i corpi delle viste.
Utilizza i result builders quando devi fornire agli utenti della tua libreria una sintassi dichiarativa per costruire strutture complesse — configurazioni, query, componenti UI — senza scrivere codice assembly imperativo.
Il compilatore Swift trasforma ogni blocco di codice contrassegnato con @resultBuilder in una sequenza di chiamate a metodi statici del builder. Diamo un'occhiata a un builder semplice che concatena stringhe:
@resultBuilder
struct StringBuilder {
static func buildBlock(_ parts: String...) -> String {
parts.joined(separator: " ")
}
}
Utilizzando questo builder — ogni stringa su una riga separata viene concatenata con uno spazio:
@StringBuilder
func greeting() -> String {
"Hello"
"World"
"from"
"Swift"
}
// Il compilatore trasforma questo in:
// StringBuilder.buildBlock("Hello", "World", "from", "Swift")
// Risultato: "Hello World from Swift"
Il compilatore raggruppa le espressioni consecutive e le passa come parametri variadici a buildBlock. Se un if appare tra le espressioni, il compilatore chiama buildOptional o buildEither per la ramificazione. Per i cicli for-in, chiama buildArray. Così, il codice Swift normale viene trasformato in una catena di chiamate che costruiscono il valore finale.
Ogni result builder definisce un insieme di metodi statici che il compilatore chiama durante la trasformazione. I metodi principali:
| Metodo | Scopo | Quando viene chiamato |
|---|---|---|
| buildBlock | Combina una sequenza di espressioni | Per ogni blocco senza ramificazione |
| buildOptional | Gestisce if senza else | Quando c'è if senza else |
| buildEither(first:) | Primo ramo di if-else | Per if con else |
| buildEither(second:) | Secondo ramo di if-else | Per if con else |
| buildArray | Gestisce i cicli for-in | Quando for-in è presente |
| buildExpression | Trasforma espressioni individuali | Per ogni espressione prima di passarla a buildBlock |
| buildFinalResult | Trasformazione finale | Prima di restituire dalla closure |
Un'implementazione minima richiede solo buildBlock con parametri variadici — questo è sufficiente per blocchi senza ramificazione. Aggiungere buildOptional e buildEither include il supporto per costrutti condizionali, e buildArray per i cicli. Secondo la Documentazione Swift (2025), si consiglia di implementare tutti i metodi per la massima flessibilità del DSL.
buildExpression consente di accettare espressioni di diversi tipi e convertirle in un unico tipo di builder. Ad esempio, in @ViewBuilder, buildExpression accetta Text, Image, Button e li converte nel tipo comune View.
Esaminiamo la creazione di un builder per costruire stringhe HTML. Questo DSL consentirà di scrivere HTML dichiarativo direttamente in Swift:
@resultBuilder
enum HTMLBuilder {
static func buildBlock(_ components: String...) -> String {
components.joined()
}
static func buildOptional(_ component: String?) -> String {
component ?? ""
}
static func buildEither(first component: String) -> String {
component
}
static func buildEither(second component: String) -> String {
component
}
static func buildArray(_ components: [String]) -> String {
components.joined()
}
}
Utilizzo del builder personalizzato per generare HTML:
func div(@HTMLBuilder _ content: () -> String) -> String {
"<div>\(content())</div>"
}
func p(_ text: String) -> String {
"<p>\(text)</p>"
}
let page = div {
p("Hello")
p("World")
if showFooter {
p("Piè di pagina")
}
}
// Risultato: <div><p>Hello</p><p>World</p><p>Footer</p></div>
Secondo l'articolo “Building Custom Result Builders in Swift” di Swift.org (2025), i builder personalizzati sono usati nelle librerie per costruire file di configurazione, componenti UI, mappatura dati e persino query di database — ovunque sia necessaria una sintassi dichiarativa con supporto di ramificazioni.
@ViewBuilder è un result builder integrato in SwiftUI che viene applicato al parametro content della maggior parte delle view contenitore: VStack, HStack, ZStack, Group, List e la proprietà body stessa. Consente di scrivere più viste su righe separate senza virgole o wrapper.
@ViewBuilder implementa tutti i metodi del result builder, inclusi il supporto per if-else, switch e for-in. Quando una condizione è vera, buildEither(first:) restituisce una vista; quando è falsa, buildEither(second:) ne restituisce un'altra. Entrambi i rami devono restituire lo stesso tipo, ma SwiftUI utilizza AnyView internamente o l'eliminazione del tipo tramite ConditionalContent.
struct GreetingView: View {
let isLoggedIn: Bool
var body: some View {
VStack {
Image(systemName: "person.circle")
Text("Profilo")
.font(.title)
if isLoggedIn {
Text("Bentornato!")
.foregroundColor(.green)
} else {
Button("Accedi") { }
}
}
}
}
Senza @ViewBuilder, lo stesso codice richiederebbe Group per ogni sezione condizionale o l'uso di AnyView, che penalizza le prestazioni. @ViewBuilder seleziona automaticamente la rappresentazione più efficiente — ConditionalContent o TupleView — per ogni combinazione.
La prima limitazione è il numero massimo di espressioni in buildBlock. La libreria standard Swift definisce overload di buildBlock per 2–10 espressioni. Se un blocco ha più di 10 espressioni, il compilatore genererà un errore. La soluzione è raggruppare tramite Group o VStack per dividere in sottoblocchi.
La seconda limitazione è la mancanza di supporto per variabili e assegnazioni all'interno del blocco del builder. Non puoi dichiarare let x = 5 all'interno di @ViewBuilder. Tutte le espressioni devono essere espressioni che restituiscono un valore del tipo del builder. Per calcoli intermedi, usa calcoli al di fuori del builder o buildExpression con supporto per diversi tipi.
La terza limitazione è la complessità di debug. Gli errori di compilazione all'interno di un result builder spesso producono messaggi confusi, specialmente quando i tipi non corrispondono nei rami if/else. Usa tipi di ritorno espliciti e AnyView per il debug, sebbene quest'ultimo riduca le prestazioni. Secondo Hacking with Swift (2025), un consiglio pratico è iniziare con un builder semplice senza ramificazioni e aggiungere gradualmente il supporto per costrutti condizionali.
Domande frequenti
Result Builder è un attributo Swift che trasforma una sequenza di espressioni in un valore risultante tramite metodi statici. Consente di creare DSL dichiarativi, l'esempio più noto è @ViewBuilder in SwiftUI per costruire gerarchie di viste senza codice imperativo.
Dichiara una struttura con l'attributo @resultBuilder e implementa almeno il metodo buildBlock. Per supportare condizioni, aggiungi buildOptional e buildEither; per i cicli, aggiungi buildArray. Usa l'attributo del builder prima del parametro closure in una funzione.
Solo buildBlock è obbligatorio. Tutti gli altri metodi — buildOptional, buildEither, buildArray, buildExpression, buildFinalResult — sono opzionali e aggiungono supporto per i costrutti corrispondenti. Più metodi vengono implementati, più il DSL è flessibile.
@ViewBuilder è un'implementazione concreta del result builder per il protocollo View. È definito in SwiftUI come una struttura con l'attributo @resultBuilder, che fornisce metodi buildBlock per diversi numeri di viste (TupleView), buildEither per ConditionalContent e buildArray per ForEach.
Sì, gli overload standard di buildBlock supportano fino a 10 espressioni. Superando questo limite, usa contenitori annidati (Group, VStack) per dividere in sottoblocchi. Un builder personalizzato può definire un buildBlock variadico senza limitazione.
Riepilogo
buildBlock, buildEither, buildOptional, buildArray a seconda dei costrutti di controlloSvilupperemo 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