Result Builder est un attribut Swift implémenté via le protocole @resultBuilder qui transforme une séquence d'expressions en une valeur composite. Le compilateur convertit les blocs de code avec des constructions de contrôle if, for, switch en appels aux méthodes statiques du builder — buildBlock, buildEither, buildArray. Selon la proposition Swift Evolution SE-0289 (2022), les result builders permettent de créer des DSL déclaratifs dans Swift sans analyseurs externes. L'exemple le plus connu est @ViewBuilder dans SwiftUI, où le corps de la vue est construit à partir d'éléments conditionnels et cycliques dans un style déclaratif.
Points clés
TupleViewif/else, switch, for-in via les méthodes buildOptional, buildEither, buildArrayResult Builder (anciennement connu sous le nom de function builders) est un mécanisme Swift qui permet de transformer une séquence d'expressions séparées par des sauts de ligne en une seule valeur. Il est déclaré à l'aide de l'attribut @resultBuilder appliqué à une structure qui implémente des méthodes de transformation statiques.
Avant les result builders, la syntaxe déclarative du body de SwiftUI était impossible. Au lieu d'une liste compacte de vues, les développeurs devaient écrire manuellement des appels TupleView. Result Builder encapsule automatiquement chaque expression, prend en charge les branchements et les boucles, cachant la complexité de la composition au développeur.
Selon Swift Evolution SE-0289, acceptée en 2022, le result builder est une évolution de l'idée des function builders (SE-0258, Swift 5.1). Changements principaux : renommage de @_functionBuilder en @resultBuilder et extension aux paramètres de fonction, permettant d'utiliser les builders pour n'importe quel argument de closure, pas seulement pour les corps de vue.
Utilisez les result builders lorsque vous devez fournir aux utilisateurs de votre bibliothèque une syntaxe déclarative pour construire des structures complexes — configurations, requêtes, composants d'interface utilisateur — sans écrire de code assembleur impératif.
Le compilateur Swift transforme chaque bloc de code marqué avec @resultBuilder en une séquence d'appels aux méthodes statiques du builder. Regardons un builder simple qui concatène des chaînes :
@resultBuilder
struct StringBuilder {
static func buildBlock(_ parts: String...) -> String {
parts.joined(separator: " ")
}
}
En utilisant ce builder — chaque chaîne sur une ligne séparée est concaténée avec un espace :
@StringBuilder
func greeting() -> String {
"Hello"
"World"
"from"
"Swift"
}
// Le compilateur transforme cela en:
// StringBuilder.buildBlock("Hello", "World", "from", "Swift")
// Résultat : "Hello World from Swift"
Le compilateur regroupe les expressions consécutives et les passe comme paramètres variadiques à buildBlock. Si un if apparaît entre les expressions, le compilateur appelle buildOptional ou buildEither pour le branchement. Pour les boucles for-in, il appelle buildArray. Ainsi, le code Swift ordinaire est transformé en une chaîne d'appels qui construisent la valeur finale.
Chaque result builder définit un ensemble de méthodes statiques que le compilateur appelle pendant la transformation. Les méthodes principales :
| Méthode | Objectif | Quand elle est appelée |
|---|---|---|
| buildBlock | Combine une séquence d'expressions | Pour chaque bloc sans branchement |
| buildOptional | Traite if sans else | Quand il y a if sans else |
| buildEither(first:) | Première branche du if-else | Pour if avec else |
| buildEither(second:) | Deuxième branche du if-else | Pour if avec else |
| buildArray | Traite les boucles for-in | Quand for-in est présent |
| buildExpression | Transforme les expressions individuelles | Pour chaque expression avant de la passer à buildBlock |
| buildFinalResult | Transformation finale | Avant de retourner de la closure |
Une implémentation minimale nécessite seulement buildBlock avec des paramètres variadiques — cela suffit pour les blocs sans branchement. Ajouter buildOptional et buildEither inclut la prise en charge des constructions conditionnelles, et buildArray pour les boucles. Selon la Documentation Swift (2025), il est recommandé d'implémenter toutes les méthodes pour une flexibilité maximale du DSL.
buildExpression permet d'accepter des expressions de différents types et de les convertir en un seul type de builder. Par exemple, dans @ViewBuilder, buildExpression accepte Text, Image, Button et les convertit en type commun View.
Examinons la création d'un builder pour construire des chaînes HTML. Ce DSL permettra d'écrire du HTML déclaratif directement en 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()
}
}
Utilisation du builder personnalisé pour générer du 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("Pied de page")
}
}
// Résultat : <div><p>Hello</p><p>World</p><p>Footer</p></div>
Selon l'article “Building Custom Result Builders in Swift” de Swift.org (2025), les builders personnalisés sont utilisés dans les bibliothèques pour construire des fichiers de configuration, des composants d'interface utilisateur, du mappage de données et même des requêtes de base de données — partout où une syntaxe déclarative avec prise en charge des branchements est nécessaire.
@ViewBuilder est un result builder intégré à SwiftUI qui est appliqué au paramètre content de la plupart des vues conteneurs : VStack, HStack, ZStack, Group, List et la propriété body elle-même. Il permet d'écrire plusieurs vues sur des lignes séparées sans virgules ni encapsulations.
@ViewBuilder implémente toutes les méthodes du result builder, y compris la prise en charge de if-else, switch et for-in. Quand une condition est vraie, buildEither(first:) retourne une vue ; quand elle est fausse, buildEither(second:) en retourne une autre. Les deux branches doivent retourner le même type, mais SwiftUI utilise AnyView en interne ou l'effacement de type via ConditionalContent.
struct GreetingView: View {
let isLoggedIn: Bool
var body: some View {
VStack {
Image(systemName: "person.circle")
Text("Profil")
.font(.title)
if isLoggedIn {
Text("Bon retour !")
.foregroundColor(.green)
} else {
Button("Connexion") { }
}
}
}
}
Sans @ViewBuilder, le même code nécessiterait Group pour chaque section conditionnelle ou l'utilisation de AnyView, ce qui nuit aux performances. @ViewBuilder sélectionne automatiquement la représentation la plus efficace — ConditionalContent ou TupleView — pour chaque combinaison.
La première limitation est le nombre maximum d'expressions dans buildBlock. La bibliothèque standard Swift définit des surcharges de buildBlock pour 2 à 10 expressions. Si un bloc contient plus de 10 expressions, le compilateur générera une erreur. La solution est le regroupement via Group ou VStack pour diviser en sous-blocs.
La deuxième limitation est l'absence de prise en charge des variables et des affectations à l'intérieur du bloc du builder. Vous ne pouvez pas déclarer let x = 5 dans @ViewBuilder. Toutes les expressions doivent être des expressions retournant une valeur du type du builder. Pour les calculs intermédiaires, utilisez des calculs en dehors du builder ou buildExpression avec prise en charge de différents types.
La troisième limitation est la complexité de débogage. Les erreurs de compilation dans un result builder produisent souvent des messages confus, en particulier lorsque les types ne correspondent pas dans les branches if/else. Utilisez des types de retour explicites et AnyView pour le débogage, bien que ce dernier réduise les performances. Selon Hacking with Swift (2025), un conseil pratique est de commencer par un builder simple sans branchement et d'ajouter progressivement la prise en charge des constructions conditionnelles.
Questions fréquentes
Result Builder est un attribut Swift qui transforme une séquence d'expressions en une valeur résultante via des méthodes statiques. Il permet de créer des DSL déclaratifs, l'exemple le plus connu étant @ViewBuilder dans SwiftUI pour construire des hiérarchies de vues sans code impératif.
Déclarez une structure avec l'attribut @resultBuilder et implémentez au moins la méthode buildBlock. Pour prendre en charge les conditions, ajoutez buildOptional et buildEither ; pour les boucles, ajoutez buildArray. Utilisez l'attribut du builder avant le paramètre closure dans une fonction.
Seulement buildBlock est obligatoire. Toutes les autres méthodes — buildOptional, buildEither, buildArray, buildExpression, buildFinalResult — sont optionnelles et ajoutent la prise en charge des constructions correspondantes. Plus il y a de méthodes implémentées, plus le DSL est flexible.
@ViewBuilder est une implémentation concrète du result builder pour le protocole View. Il est défini dans SwiftUI comme une structure avec l'attribut @resultBuilder, fournissant des méthodes buildBlock pour différents nombres de vues (TupleView), buildEither pour ConditionalContent et buildArray pour ForEach.
Oui, les surcharges standard de buildBlock prennent en charge jusqu'à 10 expressions. En cas de dépassement, utilisez des conteneurs imbriqués (Group, VStack) pour diviser en sous-blocs. Un builder personnalisé peut définir un buildBlock variadique sans limitation.
Résumé
buildBlock, buildEither, buildOptional, buildArray selon les constructions de contrôleNous développerons une application mobile clé en main
IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.
Lisez aussi