@EnvironmentObject est un property wrapper dans SwiftUI qui permet à toute vue de la hiérarchie d'accéder à un ObservableObject sans le passer explicitement via une chaîne d'initialiseurs. L'objet est injecté dans l'environnement à l'aide du modificateur .environmentObject() à un niveau spécifique de la hiérarchie, après quoi toutes les vues filles peuvent y accéder via @EnvironmentObject. Cela élimine la nécessité de passer l'objet à travers des vues intermédiaires qui ne l'utilisent pas — ce qu'on appelle le prop drilling. Selon un article de John Sundell — Swift by Sundell (2025), @EnvironmentObject est particulièrement utile pour les données inter-écrans : session utilisateur, paramètres d'application, gestionnaire de panier d'achat ou cache local de données.
Points clés
@EnvironmentObject est un property wrapper qui permet aux vues SwiftUI d'accéder à un ObservableObject depuis l'environnement de l'application. L'environnement est un conteneur dans lequel on peut placer des objets à n'importe quel niveau de la hiérarchie des vues à l'aide du modificateur .environmentObject(). Une fois qu'un objet est placé dans l'environnement, toute vue fille peut y accéder en déclarant simplement une propriété avec @EnvironmentObject et en spécifiant le type de l'objet.
L'objectif principal de @EnvironmentObject est de résoudre le problème du transfert de données à travers une hiérarchie de vues profonde sans avoir à passer l'objet par chaque niveau intermédiaire. Dans les applications complexes avec NavigationStack, TabView et fenêtres modales ramifiées, @EnvironmentObject simplifie considérablement l'architecture en éliminant le code boilerplate.
Selon Apple Developer Documentation — Environment (2025), @EnvironmentObject utilise un mécanisme interne de SwiftUI basé sur PreferenceKey et l'identification des vues. Chaque vue stocke une référence à son propre environnement, qui est hérité de la vue parente et peut être étendu à l'aide de .environmentObject(). La recherche de l'objet remonte la hiérarchie jusqu'à la vue racine.
class UserSession: ObservableObject {
@Published var isLoggedIn = false
@Published var userName: String = ""
func login(name: String) {
userName = name
isLoggedIn = true
}
}
@main
struct MyApp: App {
@StateObject var session = UserSession()
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(session)
}
}
}
@EnvironmentObject fonctionne sur la base d'un mécanisme d'injection de dépendances (DI) intégré dans SwiftUI. Lorsque vous appelez .environmentObject() sur une vue, SwiftUI stocke l'objet dans un stockage spécial associé à cette vue et à tous ses descendants. Lorsqu'une vue fille déclare @EnvironmentObject du même type, SwiftUI recherche l'objet dans l'environnement en remontant la hiérarchie des parents.
Une caractéristique importante — le type de l'objet est utilisé comme clé pour la recherche dans l'environnement. S'il y a deux objets du même type dans l'environnement, SwiftUI trouve le plus proche de la vue actuelle dans la hiérarchie. Lorsque l'objet est injecté au niveau de WindowGroup, il devient globalement disponible pour tous les écrans de l'application, ce qui est pratique pour les services à usage général.
Selon objc.io — SwiftUI Architecture (2025), @EnvironmentObject utilise en interne un mécanisme similaire à @ObservedObject, mais avec une couche d'abstraction supplémentaire pour trouver l'objet dans la hiérarchie. SwiftUI ne copie pas l'objet et n'en crée pas un nouveau — il passe une référence à l'instance existante, de sorte que les modifications apportées à l'objet sont automatiquement visibles pour toutes les vues qui utilisent @EnvironmentObject.
@EnvironmentObject et @ObservedObject remplissent tous deux la même fonction de base : ils abonnent une vue aux modifications d'un ObservableObject. La différence réside dans le mécanisme de passage de l'objet. @ObservedObject nécessite un passage explicite via un initialiseur, tandis que @EnvironmentObject récupère l'objet de l'environnement sans spécification explicite dans chaque vue intermédiaire.
| Caractéristique | @EnvironmentObject | @ObservedObject |
|---|---|---|
| Passage | Via .environmentObject() au niveau de la hiérarchie | Via l'initialiseur de chaque vue |
| Visibilité des dépendances | Masquée — non visible dans la signature de la vue | Explicite — visible dans le init de la vue |
| Vues intermédiaires | Ne connaissent pas l'objet | Doivent transmettre l'objet |
| Risque d'erreur | Plantage à l'exécution si l'objet est absent | Vérification à la compilation (si le paramètre est obligatoire) |
| Prop drilling | Élimine | Nécessite un passage manuel |
Le choix entre @EnvironmentObject et @ObservedObject dépend de l'architecture. Si l'objet est nécessaire au plus profond de la hiérarchie et sur de nombreux écrans — @EnvironmentObject est plus pratique. Si l'architecture nécessite une spécification explicite des dépendances pour les tests et la lisibilité — @ObservedObject est préférable.
Le scénario le plus courant est une session utilisateur qui doit être accessible sur tous les écrans de l'application. En injectant UserSession via .environmentObject() à la racine de l'application, n'importe quel écran peut accéder aux données utilisateur et à l'état d'autorisation.
struct ProfileView: View {
@EnvironmentObject var session: UserSession
var body: some View {
VStack {
if session.isLoggedIn {
Text("Hello, \(session.userName)")
Button("Logout") {
session.isLoggedIn = false
}
} else {
LoginView()
}
}
}
}
struct SettingsView: View {
@EnvironmentObject var session: UserSession
var body: some View {
Form {
Text("Logged in as \(session.userName)")
}
}
}
Remarquez que ni ProfileView ni SettingsView ne reçoivent la session via un initialiseur. Ils déclarent simplement @EnvironmentObject var session: UserSession, et SwiftUI trouve automatiquement l'objet dans l'environnement. Cela permet d'ajouter de nouveaux écrans sans modifier le code existant de transfert de données.
Le principal risque de @EnvironmentObject est un plantage à l'exécution si l'objet n'a pas été injecté dans l'environnement. Contrairement aux paramètres optionnels, @EnvironmentObject ne peut pas être nil. Si une vue avec @EnvironmentObject apparaît à l'écran et que la vue parente n'a pas appelé .environmentObject() pour ce type, l'application plante immédiatement avec « Fatal error: No ObservableObject of type X found ».
Si vous injectez deux objets du même type à différents niveaux de la hiérarchie, la vue fille recevra le plus proche dans la hiérarchie. Cela peut créer une confusion si le développeur s'attend à ce que l'objet de l'environnement racine soit disponible dans une fenêtre modale qui a son propre environnement avec un objet du même type.
Avec l'évolution de SwiftUI, des approches alternatives de gestion des dépendances sont apparues pour remédier à certains inconvénients de @EnvironmentObject — principalement le caractère implicite des dépendances et le risque de plantage à l'exécution.
Le choix de l'approche dépend de la taille de l'équipe et de la complexité de l'application. Pour les petits projets, @EnvironmentObject fonctionne très bien. Pour les grands projets avec des dizaines d'écrans et des exigences de test strictes, le passage explicite via @ObservedObject ou un conteneur DI est préférable.
Foire aux questions
Oui, une vue peut déclarer autant de @EnvironmentObject de différents types que nécessaire. SwiftUI recherche chaque type indépendamment dans l'environnement. C'est pratique lorsqu'une vue a besoin d'accéder simultanément à la session utilisateur, aux paramètres et au panier d'achat — chaque objet est injecté séparément.
Le Preview plante avec une erreur d'exécution en essayant d'afficher la vue. Ajoutez toujours .environmentObject() dans Preview pour les vues qui utilisent @EnvironmentObject. Utilisez des objets factices avec des données de test pour que le Preview fonctionne correctement et affiche un état réaliste.
Non, @EnvironmentObject fonctionne uniquement avec un type de classe concret conforme à ObservableObject. Pour les protocoles, vous devez utiliser l'effacement de type ou un wrapper : créez une classe wrapper qui contient une référence à l'objet typé par protocole et injectez le wrapper via @EnvironmentObject.
Créez une instance d'ObservableObject avec des données de test et passez-la à la vue via .environmentObject(testObject) dans le test. C'est le modèle standard pour les tests d'interface utilisateur SwiftUI. Pour les tests unitaires, isolez la logique dans l'ObservableObject et testez-le séparément de la vue.
@EnvironmentObject ne crée pas de charge de performance supplémentaire car il ne fait que passer une référence à l'objet, pas une copie. Cependant, des mises à jour fréquentes des propriétés @Published dans un objet global peuvent entraîner le redessinage simultané de nombreuses vues, ce qui peut affecter les performances.
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