some View est une construction syntaxique clé de Swift sans laquelle SwiftUI ne peut pas fonctionner. Selon Apple Swift Book, 2024, some View est un type opaque qui cache le type de retour spécifique tout en maintenant un typage strict au moment de la compilation. Cette construction permet au protocole View d’avoir une signature body unifiée sans révéler les détails d’implémentation.
Points clés
some View est une syntaxe de type opaque introduite dans Swift 5.1. Elle est utilisée comme type de retour de la propriété body du protocole View. La notation some View signifie : « la fonction ou propriété retourne un type concret qui se conforme au protocole View, mais le code appelant ne sait pas et ne doit pas savoir lequel ».
Le concept de type opaque est le revers de la programmation générique (generics). Si les generics permettent au code appelant de déterminer le type, le type opaque permet à l’implémentation de déterminer le type, en le cachant à l’appelant. Cela donne au développeur la liberté de modifier l’implémentation interne sans changer le contrat.
Selon Swift Evolution SE-0244, les types opaques ont été ajoutés pour supporter SwiftUI et le motif des protocoles avec types associés (PAT), qui ne peuvent pas être utilisés comme type de retour sans cette construction.
Sans some View, la signature body serait impossible : le protocole View a un type associé Body qui se conforme à View. Si body retournait simplement View (comme protocole), Swift ne pourrait pas travailler avec des protocoles ayant des exigences Self en position de retour. some View résout ce problème en fournissant un type concret mais caché.
Le type opaque est un type spécial qui se comporte comme concret pour le compilateur mais comme abstrait pour le développeur. Lorsque le compilateur voit some View, il analyse l’implémentation et détermine le type de retour exact. Ce type est fixé et utilisé pour la génération de code sans dispatch dynamique.
struct SimpleView: View {
var body: some View {
Text("Bonjour")
}
}
// Le compilateur voit : body -> Text, not some View
Principe de fonctionnement : Le compilateur Swift déduit le type concret de l’implémentation. Dans l’exemple ci-dessus, le corps contient uniquement Text, donc le compilateur sait que body retourne exactement Text, même si la signature est écrite comme some View. Cela offre deux optimisations : appel direct sans table de méthodes virtuelles et possibilité d’inlining.
Si l’implémentation de body change (par exemple, au lieu de Text, un VStack de Text et Button est retourné), le compilateur redéfinit le type concret. Mais pour le code appelant (SwiftUI), la signature reste la même — some View. C’est le revers des generics : le code appelant ne dépend pas des changements d’implémentation.
Une des règles clés des types opaques : une fonction ou propriété retournant some View doit toujours retourner le même type concret. On ne peut pas retourner Text dans une branche if et Image dans une autre. Cette limitation est vérifiée par le compilateur et sert de garantie pour le code appelant.
struct BadView: View {
var flag: Bool
var body: some View {
if flag {
Text("Vrai") // Erreur : Text vs VStack
} else {
VStack {
Text("Faux")
Image(systemName: "xmark")
}
}
}
}
Pour résoudre ce problème, on utilise @ViewBuilder, qui enveloppe différentes branches dans un conteneur conditionnel ConditionalContent. L’annotation @ViewBuilder sur body est une pratique standard dans SwiftUI, bien qu’elle puisse être implicite si body ne contient qu’une seule expression.
AnyView est un type qui efface l’implémentation concrète de View (type erasure). Il enveloppe n’importe quel View dans un unique emballage, permettant de stocker des Views de différents types dans le même conteneur. Contrairement à some View, AnyView fonctionne à l’exécution et ajoute une surcharge d’empaquetage et de dépaquetage.
| Critère | some View | AnyView |
|---|---|---|
| Temps de résolution | compilation | exécution |
| Performances | appel direct, sans surcharge | empaquetage dans un existential container |
| Flexibilité des types | un seul type concret | tout type View |
| Changement dynamique | non supporté | supporté à l’exécution |
| Priorité d’utilisation | toujours quand possible | seulement quand some View est impossible |
| Support des protocoles PAT | oui | oui |
Quand utiliser AnyView : uniquement dans les situations où some View est impossible en raison de la nécessité d’un changement dynamique de type à l’exécution. Par exemple, lors du retour d’un View depuis un dictionnaire ou dans une structure récursive où le type concret doit changer à chaque niveau. AnyView doit être minimisé, car chaque empaquetage désactive les optimisations de SwiftUI.
Idée reçue courante : AnyView ne résout pas le problème des différents types dans body — c’est @ViewBuilder qui le résout. AnyView efface le type mais n’aide pas le compilateur à déduire un type unique. Utilisez @ViewBuilder pour la logique conditionnelle et AnyView uniquement pour le dispatch dynamique.
@ViewBuilder est un result builder conçu spécifiquement pour travailler avec some View. Il permet d’utiliser une logique conditionnelle (if/else, switch) et des expressions multiples dans le body tout en maintenant un type de retour unique. ViewBuilder enveloppe automatiquement les expressions multiples dans TupleView et les branches conditionnelles dans ConditionalContent.
struct ProfileView: View {
let user: User?
@ViewBuilder
var body: some View {
if let user {
UserCard(user: user)
Text("En ligne")
.font(.caption)
} else {
ProgressView("Loading...")
}
}
}
Comment ça fonctionne : @ViewBuilder analyse le bloc de code et génère l’appel approprié buildBlock, buildOptional ou buildEither. Pour la logique conditionnelle, ConditionalContent est créé — un type commun qui cache les types concrets à l’intérieur des branches mais est lui-même un type unique pour le compilateur. Cela résout le problème des différents types concrets.
Sans @ViewBuilder, une propriété body contenant des expressions multiples ou une logique conditionnelle provoquerait une erreur de compilation. C’est pourquoi SwiftUI applique @ViewBuilder à body implicitement, et pour les propriétés et fonctions personnalisées, il doit être ajouté explicitement.
@ViewBuilder peut être imbriqué : un ViewBuilder à l’intérieur d’un autre. Cela permet de créer des hiérarchies complexes avec des conditions à différents niveaux. Cependant, une imbrication profonde complique la lisibilité, il est donc recommandé d’extraire les conditions imbriquées dans des composants View séparés.
Exemple 1 : retourner un View personnalisé depuis une propriété calculée. Une propriété peut retourner some View, cachant la composition interne. Cela permet de réorganiser le code sans modifier l’interface publique.
struct ArticleView: View {
var body: some View {
CardView {
HeaderView()
ContentView()
FooterView()
}
}
}
struct CardView<Content: View>: View {
let content: Content
var body: some View {
content
.padding(16)
.background(.white)
.cornerRadius(12)
.shadow(radius: 4)
}
}
Exemple 2 : passer un View comme closure via @ViewBuilder. Ce motif est utilisé dans les conteneurs standard de SwiftUI (VStack, HStack, List) et peut être implémenté dans des composants personnalisés.
struct CustomContainer<Content: View>: View {
@ViewBuilder let content: () -> Content
var body: some View {
VStack(alignment: .leading) {
content()
}
.padding(20)
}
}
Exemple 3 : une fonction d’usine qui retourne some View. Permet de créer des Views en fonction de paramètres sans révéler l’implémentation. C’est particulièrement utile pour les bibliothèques et les composants réutilisables.
func makeIcon(for status: Status) -> some View {
switch status {
case .success:
Image(systemName: "checkmark.circle.fill")
.foregroundColor(.green)
case .error:
Image(systemName: "xmark.circle.fill")
.foregroundColor(.red)
case .pending:
ProgressView()
}
}
Foire aux questions
some View est un type opaque, ce qui signifie qu’un type concret conforme au protocole View est retourné. Le type concret est fixé par le compilateur mais caché au code appelant. Cela assure un typage strict sans révéler les détails d’implémentation.
some View est résolu à la compilation avec une surcharge nulle. AnyView utilise le type erasure à l’exécution avec des coûts supplémentaires d’empaquetage dans un existential container. Utilisez some View autant que possible, AnyView uniquement pour le changement dynamique de type.
Un type opaque nécessite un type concret unique pour tous les chemins de retour. if/else avec différents types viole cette exigence. @ViewBuilder résout le problème en enveloppant les branches dans ConditionalContent — un type unique qui cache les différences des implémentations concrètes.
some View ne réduit pas les performances — le compilateur connaît le type exact et génère du code direct. En revanche, any View (comme protocole) nécessiterait un dispatch dynamique. some View est un mécanisme d’optimisation intégré dans la conception de SwiftUI.
Oui, some est une construction générale de Swift 5.1 non liée à SwiftUI. Elle peut être utilisée avec n’importe quel protocole : some Equatable, some Codable, some Collection. C’est utile pour cacher des types imbriqués complexes comme [String: [Int]].
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