La propriété body est l'élément central du protocole View dans SwiftUI, déterminant quel contenu est affiché à l'écran. Selon Apple Developer Documentation, 2024, body est la seule exigence obligatoire du protocole View et retourne un type qui est conforme à ce même protocole. SwiftUI appelle body à chaque changement d'état pour construire et comparer une nouvelle arborescence d'éléments.
Points Clés
body est une propriété calculée qui est la seule exigence obligatoire du protocole View. Toute structure conforme à View doit implémenter body. La propriété retourne le contenu que SwiftUI affiche à l'écran — cela peut être du texte, une image, un bouton, un conteneur avec des éléments imbriqués ou tout autre type conforme au protocole View.
La signature de body est toujours fixe : var body: some View { get }. Le type de retour est some View (un type opaque), pas un type concret. Cela signifie que différentes Views peuvent retourner différents types concrets dans body, mais le compilateur Swift fixe le type concret pour chaque implémentation au moment de la compilation.
Selon la WWDC 2022, body est le point d'entrée pour la description déclarative de l'interface. Contrairement à UIKit, où vous créez et configurez UIView de manière impérative, dans SwiftUI vous décrivez de manière déclarative ce qui doit être affiché, et SwiftUI lui-même calcule comment l'implémenter.
body doit se comporter comme une fonction pure — avec les mêmes entrées (propriétés de la structure et état) elle doit retourner la même arborescence View. Si body dépend d'un état mutable externe (variables globales, UserDefaults sans le wrapper @AppStorage), le comportement devient imprévisible et SwiftUI peut redessiner l'écran de manière incorrecte.
La propriété calculée body ne stocke pas de valeur — elle est calculée à chaque accès. Lorsque SwiftUI détermine que l'état a changé, il recrée la structure View et lit la nouvelle valeur de body pour obtenir l'arborescence d'éléments actuelle à afficher.
struct CounterView: View {
@State private var count = 0
var body: some View {
VStack {
Text("Compteur : \(count)")
.font(.largeTitle)
Button("Incrémenter") {
count += 1
}
.padding()
.background(.blue)
.foregroundColor(.white)
.cornerRadius(8)
}
}
}
Dans cet exemple, body retourne un VStack contenant un Text et un bouton avec des modificateurs. Lorsque le bouton est pressé, la propriété @State count est incrémentée, SwiftUI recrée la structure CounterView et appelle à nouveau body pour obtenir l'arborescence mise à jour avec la nouvelle valeur du Text.
Les modificateurs (.font, .padding, .background, .foregroundColor, .cornerRadius) ne modifient pas la View originale mais l'enveloppent dans ModifiedContent — un nouveau type qui ajoute la modification. Chaque modificateur crée un autre niveau d'imbrication, ce qui est important à considérer pour les performances.
some View dans le type de retour de body n'est pas seulement une convention mais une exigence du compilateur. Swift exige que tous les chemins de retour dans body aient le même type concret. Sans @ViewBuilder, vous ne pouvez pas retourner Text dans une branche et Button dans une autre — le compilateur produira une erreur.
struct ConditionalView: View {
var isReady: Bool
@ViewBuilder
var body: some View {
if isReady {
Text("Prêt")
.foregroundColor(.green)
} else {
ProgressView()
}
}
}
@ViewBuilder sur body permet d'utiliser une logique conditionnelle (if/else, switch) sans erreurs de compilation. ViewBuilder enveloppe automatiquement les différentes branches dans ConditionalContent — un type spécial qui cache les différences de types concrets. C'est une capacité clé pour construire des interfaces dynamiques.
Sans @ViewBuilder, le compilateur tente d'inférer un seul type pour tous les chemins de retour. Si les types diffèrent — une erreur se produit. C'est pourquoi SwiftUI applique implicitement @ViewBuilder à body dans les déclarations View, bien que dans le code utilisateur, vous deviez ajouter explicitement l'annotation pour les méthodes et propriétés personnalisées qui retournent plusieurs Views.
Utiliser some View au lieu d'un type concret ne réduit pas les performances — le compilateur connaît le type exact au moment de la compilation et génère du code direct sans dispatch dynamique. AnyView, au contraire, utilise l'effacement de type (type erasure) avec la surcharge d'encapsulation dans un conteneur existentiel.
body est appelée par SwiftUI dans trois scénarios principaux : lors du premier affichage de la View, lors du changement de @State/@Binding/@ObservedObject/@StateObject, et lorsque la View parent transmet de nouvelles valeurs via l'initialiseur. SwiftUI peut également appeler body lors du changement de valeurs d'environnement (@Environment).
La fréquence des appels à body ne devrait pas vous inquiéter — SwiftUI optimise le redessin grâce au mécanisme d'identité. Chaque View dans la hiérarchie a un identifiant unique. Si l'identité et les données d'entrée n'ont pas changé — body n'est pas appelée même si la View parent est redessinée. Ceci est réalisé par la comparaison Equatable et la stabilité structurelle.
struct ParentView: View {
var body: some View {
ChildView(name: "Alice") // Identité stable
}
}
struct ChildView: View {
let name: String
var body: some View {
Text("Bonjour, \(name) !")
}
}
Dans cet exemple, si ParentView est redessinée mais transmet la même valeur name — ChildView.body n'est pas appelée. SwiftUI compare les données d'entrée de la structure et, si inchangées, ignore le redessin du composant enfant. C'est le mécanisme de différenciation de vues.
Plusieurs pièges entraînent des appels inattendus de body : l'utilisation de classes sans ObservableObject, le passage de fermetures créées à l'intérieur de body (chaque création de fermeture donne une nouvelle identité), et l'utilisation incorrecte d'EquatableView. Si body est appelée trop souvent — vérifiez la stabilité d'identité de tous les composants enfants.
Première règle : body doit être minimal. Déplacez la logique complexe dans des propriétés calculées ou des méthodes séparées qui retournent View. Cela améliore la lisibilité et permet à SwiftUI de déterminer plus précisément quelles parties de la hiérarchie ont changé. Divisez les grands bodies en sous-composants avec des limites de responsabilité claires.
Deuxième règle : n'utilisez pas body pour effectuer du travail. Chargement de données, opérations réseau, écritures en base de données — tout cela doit se produire en dehors de body, dans des tâches (tasks), des modificateurs onChange ou via ObservableObject. body est destiné uniquement à la déclaration de l'interface.
Troisième règle : utilisez la propriété EquatableView ou un protocole Equatable personnalisé pour les Views si la comparaison structurelle standard est insuffisante. Cela permet d'indiquer explicitement à SwiftUI quand une View enfant nécessite un redessin et d'éviter les appels inutiles à body.
Quatrième règle : si body contient des calculs complexes (formatage, filtrage, tri) — utilisez @State pour la mise en cache du résultat ou déplacez les calculs dans une méthode séparée appelée depuis onChange. Des calculs répétés dans body à chaque mise à jour d'état sont une cause fréquente de ralentissement des animations.
Cinquième règle : pour les listes (List, ForEach) assurez des identifiants stables via le paramètre id. Sans identité stable, ForEach recrée tous les éléments à tout changement, appelant body pour chacun d'eux, même si un seul élément a changé.
Questions Fréquentes
body est la propriété calculée du protocole View qui retourne le contenu à afficher. C'est la seule exigence obligatoire du protocole. Le type de retour est some View, ce qui permet à SwiftUI d'optimiser la hiérarchie au moment de la compilation.
Oui, SwiftUI appelle body à chaque changement d'état (@State, @Binding, @ObservedObject) ou de données d'entrée. C'est un comportement normal pour un framework déclaratif. SwiftUI optimise la fréquence des appels via le mécanisme d'identité et la comparaison Equatable.
some View est un type opaque qui cache l'implémentation concrète. Le compilateur fixe le type au moment de la compilation, garantissant des performances d'appel direct. Cela offre de la flexibilité : vous pouvez changer le type de retour sans modifier la signature.
Non, body ne peut pas être optionnelle — le type de retour some View n'autorise pas nil. Si vous devez masquer un élément conditionnellement, utilisez une logique conditionnelle dans @ViewBuilder ou retournez EmptyView, qui ne prend pas de place dans la hiérarchie.
Chaque modificateur crée une nouvelle couche ModifiedContent, augmentant la profondeur de la hiérarchie. Pour la plupart des écrans (jusqu'à 50 modificateurs), l'impact est négligeable. Un nombre excessif de modificateurs (centaines) peut ralentir le diffing. Regroupez les modificateurs associés dans des extensions personnalisées.
Résumé
Nous 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