.onAppear est un modificateur SwiftUI qui exécute une closure lorsqu’une View est ajoutée à la hiérarchie de l’interface. L’appel se produit une fois par apparition de l’instance à l’écran et sert de point principal pour charger des données, lancer des animations et envoyer des événements d’analyse. Selon la Apple Developer Documentation (2026), onAppear garantit l’exécution avant le premier rendu, mais ne garantit pas l’appel à chaque affichage répété si la View reste en mémoire. En savoir plus sur SwiftUI dans l’article sur SwiftUI.
Points clés
.onAppear est un modificateur de View dans SwiftUI qui prend une closure Void et l’exécute lorsque la View devient visible à l’écran. Ce modificateur fait partie du système de cycle de vie des composants SwiftUI, aux côtés de .onDisappear et .task. Apple a présenté onAppear avec la sortie de SwiftUI dans iOS 13 et watchOS 6 comme remplacement de viewDidLoad d’UIKit.
Syntaxiquement, .onAppear modifie n’importe quelle View et renvoie la même View avec une action attachée. Le compositeur SwiftUI appelle la closure transmise une fois lorsque la view est ajoutée à la hiérarchie et passe l’étape de rendu. Si une View est supprimée puis rajoutée (par exemple, lors du défilement dans une liste), onAppear est appelé à nouveau — ce comportement devient souvent une source de bugs inattendus.
La syntaxe de base du modificateur est minimaliste : onAppear sans paramètres. SwiftUI n’offre aucun moyen de passer une priorité ou une animation — la closure s’exécute de manière synchrone sur le thread principal immédiatement après le rendu.
struct ContentView: View {
var body: some View {
Text("Hello, SwiftUI!")
.onAppear {
print("View appeared on screen")
}
}
}
Limitations : onAppear ne prend pas en charge async/await directement. Pour les opérations asynchrones dans la closure, il faut Task {} ou une fonction async/await séparée appelée via Task.detached. Cela rend onAppear moins pratique pour les requêtes réseau par rapport au modificateur .task.
.onAppear est intégré dans le pipeline de rendu SwiftUI à l’étape layout+render. Lorsque SwiftUI calcule le corps de la View et détecte un changement de hiérarchie, il déclenche les callbacks onAppear pour toutes les views nouvellement ajoutées. L’ordre d’appel suit l’imbrication : onAppear du parent en premier, puis les éléments enfants.
Une caractéristique importante de SwiftUI est que onAppear n’est pas lié à l’apparition physique à l’écran. Le modificateur est appelé lorsqu’une View est ajoutée à la hiérarchie, qu’elle soit visible ou non pour l’utilisateur (par exemple, hors écran dans un ScrollView). Cela distingue SwiftUI d’UIKit, où viewWillAppear n’est déclenché qu’à l’apparition réelle.
L’ordre d’appel suit la règle parent-d’abord : VStack ou NavigationView reçoit onAppear en premier, puis chaque élément enfant dans l’ordre. C’est crucial pour l’initialisation des ressources partagées : si les éléments enfants dépendent de données chargées par le parent, ils doivent vérifier la disponibilité via Optional.
struct ParentView: View {
var body: some View {
VStack {
ChildView()
ChildView()
}
.onAppear {
print("Parent onAppear — first")
}
}
}
struct ChildView: View {
var body: some View {
Text("Child")
.onAppear {
print("Child onAppear")
}
}
}
La sortie console sera : Parent onAppear — d’abord, puis Child onAppear deux fois dans l’ordre. Ce comportement est garanti par Apple et stable dans toutes les versions de SwiftUI (iOS 13–18).
.onAppear a plusieurs scénarios d’appel qui dépendent du conteneur et de la navigation. Dans NavigationStack, onAppear se déclenche à chaque push d’un nouveau contrôleur et au pop — pour le contrôleur racine. Dans TabView, le changement d’onglet appelle onAppear pour l’onglet affiché et onDisappear pour l’onglet caché.
Dans List et ScrollView, onAppear est appelé pour les cellules qui sont entrées dans la zone visible ou qui se trouvent dans le tampon de pré-rendu. iOS 18 a introduit un mécanisme de prefetch qui peut appeler onAppear pour les cellules situées 2–3 écrans avant le défilement — cela accélère la perception mais peut provoquer des requêtes réseau inutiles.
NavigationStack (iOS 16+) gère la pile d’écrans différemment de NavigationView. Lors du push d’un nouvel écran, onAppear se déclenche uniquement sur le nouvel écran, tandis que l’écran actuel ne reçoit pas onDisappear jusqu’à sa suppression effective. Au pop, le processus inverse se produit : onDisappear sur l’écran quitté, onAppear sur l’écran de retour.
| Scénario | onAppear | onDisappear |
|---|---|---|
| Push | Nouvel écran | Non (l’écran reste dans la pile) |
| Pop | Écran de retour | Écran quitté |
| Changement d’onglet | Nouvel onglet | Ancien onglet |
| Fermeture de sheet | Écran parent | Sheet ouvert |
Les applications pratiques de onAppear couvrent trois catégories principales : chargement de données, lancement d’animations et envoi d’analytique. Chaque scénario nécessite de prendre en compte les caractéristiques du cycle de vie de SwiftUI pour éviter les appels en double et les fuites mémoire.
Le chargement de données est le cas d’utilisation le plus courant de onAppear. À l’intérieur de la closure, une Task est créée pour l’appel async, et le résultat est stocké dans @State ou @StateObject. Il est important de vérifier si les données ont déjà été chargées en utilisant un flag isLoading ou une vérification de nil.
struct ProfileView: View {
@StateObject private var viewModel = ProfileViewModel()
var body: some View {
VStack {
if viewModel.isLoading {
ProgressView()
} else {
Text(viewModel.userName)
}
}
.onAppear {
guard viewModel.userName == nil else { return }
Task {
await viewModel.loadProfile()
}
}
}
}
La protection contre le re-fetch est une pratique critique. Si SwiftUI recrée la View (par exemple, lors d’une rotation d’écran), onAppear se déclenchera à nouveau sans protection. Une alternative est le modificateur .task, qui annule automatiquement la requête précédente.
L’animation d’entrée utilise onAppear pour modifier les variables d’état qui déclenchent l’animation via withAnimation ou le modificateur animation. Motif typique : état initial (opacity 0, offset 100), transition vers l’état final (opacity 1, offset 0) à l’apparition.
struct AnimatedCard: View {
@State private var isVisible = false
var body: some View {
RoundedRectangle(cornerRadius: 12)
.fill(Color.blue)
.opacity(isVisible ? 1 : 0)
.offset(y: isVisible ? 0 : 50)
.animation(.spring(), value: isVisible)
.onAppear {
withAnimation(.spring().delay(0.3)) {
isVisible = true
}
}
}
}
Le délai de 0,3 seconde crée un effet d’apparition séquentielle si plusieurs cartes sont présentes à l’écran. Pour une liste d’éléments animés, utilisez l’indice de l’élément comme multiplicateur de délai.
.task est un modificateur SwiftUI ajouté dans iOS 15 qui résout le problème des opérations asynchrones dans onAppear. Contrairement à onAppear, .task accepte une closure async, gère automatiquement son cycle de vie et l’annule lorsque la View disparaît. Alors que onAppear s’exécute de manière synchrone, .task lance une opération asynchrone et permet à SwiftUI de l’annuler lors de onDisappear.
La principale différence est la gestion de l’annulation. Lorsque .task crée une opération async, SwiftUI sauvegarde une référence vers la Task et appelle automatiquement cancel() lorsque la View est supprimée de la hiérarchie. onAppear avec Task {} à l’intérieur n’annule pas l’opération en cours — elle continue de s’exécuter même après la disparition de la View, ce qui peut entraîner des conditions de course ou des écritures dans une instance désallouée.
| Caractéristique | .onAppear | .task |
|---|---|---|
| Version iOS | iOS 13+ | iOS 15+ |
| Support async | Uniquement via Task {} | Async/await natif |
| Auto-annulation | Non | À la disparition de la View |
| Réappel | À chaque apparition | Unique par défaut |
| Code synchrone | Oui | Async uniquement |
Choix du modificateur : pour les actions synchrones (animations, analytique, journalisation), utilisez onAppear. Pour le chargement de données asynchrone (API, Core Data, système de fichiers), préférez .task — c’est plus sûr et plus propre.
Erreur 1 : appels multiples dus à la recréation de View. Lorsque SwiftUI recrée le corps de la View (changement d’état, rotation d’écran), onAppear peut être appelé à nouveau. Solution — ajoutez un flag de chargement ou utilisez .equatable() pour éviter les redessinages inutiles. Selon SwiftLee (2025), 40 % des bugs SwiftUI en production sont liés à des appels répétés de onAppear.
Erreur 2 : fuite mémoire par référence forte. Si la closure de onAppear capture self sans référence faible, elle crée un cycle de rétention avec la View. SwiftUI ne garantit pas la nullification des objets capturés lors de la disparition de la View. Utilisez la capture list [weak self] pour ViewModel ou les services.
Erreur 3 : exécution sur un thread d’arrière-plan. onAppear s’exécute sur le thread principal — c’est correct pour les opérations UI. Mais si vous lancez une Task à l’intérieur de onAppear, assurez-vous que la mise à jour de @State se fait via MainActor.run. Swift 5.9 et supérieur revient automatiquement sur MainActor, mais il est préférable de spécifier @MainActor explicitement.
Le motif avec un flag de chargement est le moyen le plus fiable de se protéger contre les duplications. Stockez le flag dans @State ou @StateObject et ne le réinitialisez que lors d’une mise à jour manuelle. Une alternative est d’utiliser .task au lieu de onAppear : .task ne redémarre pas au redessinage par défaut si l’opération async est déjà en cours.
struct SafeView: View {
@State private var hasAppeared = false
@State private var items: [Item] = []
var body: some View {
List(items, id: \.id) { item in
Text(item.name)
}
.onAppear {
guard !hasAppeared else { return }
hasAppeared = true
Task {
items = await DataService.shared.fetchItems()
}
}
}
}
Foire aux questions
viewDidLoad est appelé une fois pendant la durée de vie du UIViewController, indépendamment de la visibilité. .onAppear est appelé chaque fois qu’une View est ajoutée à la hiérarchie — si une View est supprimée puis rajoutée, onAppear se déclenche à nouveau. Dans NavigationView, viewDidLoad est appelé lors de l’initialisation, tandis que onAppear est appelé à chaque affichage de l’écran.
Oui, via un wrapper Task { await asyncFunction() }. Cependant, pour les opérations async, .task est préférable car il gère automatiquement l’annulation et ne nécessite pas de créer une Task manuellement. .task garantit également l’annulation lors de la disparition de la View, prévenant les fuites.
La cause est la recréation du corps de la View en raison de modifications de @State, @Published ou de la configuration d’un ancêtre. SwiftUI peut redessiner une View en réponse à des changements dans toute propriété observable. De plus, LazyVStack et List appellent onAppear pour les cellules qui s’approchent de la zone visible, et à nouveau lors du défilement vers le haut.
Oui, .onAppear est disponible sur toutes les plateformes SwiftUI : iOS 13+, watchOS 6+, tvOS 13+, macOS 10.15+. Le comportement est identique : le modificateur est appelé lorsqu’une View est ajoutée à la hiérarchie. Sur watchOS, onAppear se déclenche lors de l’activation de l’application depuis l’état de veille, ce qui doit être pris en compte dans la conception.
.onAppear n’accepte pas de paramètres — seulement une closure Void. Pour passer des paramètres, utilisez une closure qui capture des variables externes. Une approche alternative consiste à créer un modificateur onAppear personnalisé avec des paramètres via ViewModifier ou un équivalent de .onChange.
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