@ObservedObject : qu’est-ce que c’est, observation des objets et mise à jour des vues

Auteur : IT Sectr Publié le : 2026-06-26 Temps de lecture : 9 min

@ObservedObject est un property wrapper dans SwiftUI qui permet à une View d’observer les changements dans un ObservableObject créé ailleurs dans la hiérarchie. Contrairement à @StateObject, @ObservedObject ne crée pas l’objet — il s’abonne seulement à son publisher objectWillChange et redessine la View lorsque les propriétés publiées sont mises à jour. Cela fait de @ObservedObject le bon choix pour les Views enfants qui reçoivent des données d’un parent via un initialiseur. Selon un article de Paul Hudson — Hacking with Swift (2025), une architecture typique d’application SwiftUI se construit ainsi : la View racine utilise @StateObject pour créer un view model, et toutes les Views enfants le reçoivent via @ObservedObject, garantissant une source unique de vérité sans duplication de données.

Points clés

  • @ObservedObject — un property wrapper pour observer un ObservableObject créé dans une View parente.
  • Ne possède pas l’objet — contrairement à @StateObject, @ObservedObject ne gère pas le cycle de vie de l’objet.
  • S’abonne aux changements — lorsque les propriétés @Published changent, la View est automatiquement redessinée.
  • Transmis via initialiseur — l’objet est transmis à la View enfant via un paramètre d’initialiseur.
  • iOS 13+ — @ObservedObject est disponible depuis la première version de SwiftUI, contrairement à @StateObject (iOS 14+).

Qu’est-ce que @ObservedObject dans SwiftUI

@ObservedObject est un property wrapper qui abonne une View aux changements ObservableObject. Lorsqu’un objet marqué avec @ObservedObject modifie l’une de ses propriétés déclarées avec @Published, SwiftUI redessine automatiquement la View. @ObservedObject ne crée pas l’objet — il établit seulement une connexion entre une instance ObservableObject existante et la View qui doit réagir à ses changements.

La différence clé entre @ObservedObject et @StateObject est la propriété. @ObservedObject suppose que l’objet est créé et stocké quelque part plus haut dans la hiérarchie des Views. La View enfant reçoit une référence à cet objet via l’initialiseur et l’observe simplement. Si la View enfant est recréée, elle reçoit la même référence du parent — les données ne sont pas perdues.

Selon la Documentation Apple Developer — SwiftUI (2025), @ObservedObject est disponible à partir d’iOS 13, ce qui en fait la seule option pour observer ObservableObject dans les projets prenant en charge les anciennes versions d’iOS. Dans iOS 14+, @StateObject est préféré pour créer des objets, mais @ObservedObject reste pertinent pour transmettre des objets existants.

Comment fonctionne @ObservedObject

Le mécanisme de @ObservedObject est basé sur le protocole ObservableObject du framework Combine. Chaque classe conforme à ObservableObject obtient automatiquement un publisher objectWillChange qui envoie un signal avant toute modification d’une propriété @Published. SwiftUI s’abonne à ce publisher via @ObservedObject et, à la réception du signal, marque la View comme nécessitant un redessin.

swift
class TaskViewModel: ObservableObject {
    @Published var tasks: [Task] = []
    @Published var isLoading = false
    
    func loadTasks() async {
        isLoading = true
        // récupérer les données
        isLoading = false
    }
}

struct TaskListView: View {
    @ObservedObject var viewModel: TaskViewModel
    
    var body: some View {
        List(viewModel.tasks) { task in
            Text(task.title)
        }
        .task { await viewModel.loadTasks() }
    }
}

Lorsque la View parent transmet viewModel à TaskListView via l’initialiseur, SwiftUI crée une connexion entre l’objet et la View. Lorsque le tableau tasks ou le flag isLoading change, SwiftUI redessine TaskListView. L’objet lui-même reste inchangé — il est stocké dans la View parent via @StateObject.

@ObservedObject vs @StateObject : quand utiliser quoi

La différence entre @ObservedObject et @StateObject est la différence entre un observateur et un propriétaire. @StateObject crée l’objet et gère son cycle de vie. @ObservedObject observe seulement un objet qui a été créé et stocké ailleurs. Le choix entre eux est déterminé par la responsabilité de la View vis-à-vis des données.

ScénarioRecommandationRaison
La View crée des données@StateObjectLa View possède l’objet et est responsable de son cycle de vie
La View reçoit des données@ObservedObjectLa View observe seulement, l’objet vit dans le parent
Support iOS 13@ObservedObject@StateObject indisponible, utilisez @ObservedObject avec gestion manuelle
Composant réutilisable@ObservedObjectLe composant ne doit pas créer de données — il les reçoit de l’extérieur

La règle principale : si la View crée l’objet — @StateObject. Si la View reçoit l’objet — @ObservedObject. Violer cette règle en utilisant @ObservedObject pour créer un objet entraîne une perte de données lors de la reconstruction de la View. La violer en utilisant @StateObject pour recevoir un objet crée une instance dupliquée indépendante du parent.

Exemples d’utilisation de @ObservedObject

Un scénario d’utilisation typique de @ObservedObject est une liste de tâches où la View racine crée un view model et chaque cellule de liste le reçoit via @ObservedObject. Chaque cellule peut appeler des méthodes du view model, et les changements sont automatiquement reflétés dans toute la liste puisque toutes les cellules observent le même objet.

swift
struct TaskRow: View {
    @ObservedObject var viewModel: TaskViewModel
    let task: Task
    
    var body: some View {
        HStack {
            Text(task.title)
            Spacer()
            Button("Terminé") {
                viewModel.completeTask(task)
            }
        }
    }
}

struct TaskListContainer: View {
    @StateObject var viewModel = TaskViewModel()
    
    var body: some View {
        List(viewModel.tasks) { task in
            TaskRow(viewModel: viewModel, task: task)
        }
    }
}

Dans cet exemple, TaskListContainer crée viewModel via @StateObject, et chaque TaskRow le reçoit via @ObservedObject. Lorsque l’utilisateur appuie sur « Done » dans n’importe quelle ligne, viewModel.completeTask modifie une propriété publiée, et toutes les Views observant cet objet se mettent à jour automatiquement.

Erreurs courantes avec @ObservedObject

L’erreur la plus courante est d’utiliser @ObservedObject pour créer un objet à l’intérieur d’une View. Lorsque la View est reconstruite (par exemple, lorsque l’état change), SwiftUI crée une nouvelle instance ObservableObject, entraînant la perte de toutes les données accumulées. Cette erreur est particulièrement douloureuse dans NavigationStack, où un utilisateur pourrait remplir un formulaire et perdre les données en revenant en arrière.

Erreur : @ObservedObject au lieu de @StateObject

swift
// ❌ Perte de données : @ObservedObject ne conserve pas l’objet
struct FormView: View {
    @ObservedObject var formVM = FormViewModel()
    // Nouveau formVM créé à chaque reconstruction de vue !
}

// ✅ Correct : @StateObject conserve l’objet
struct FormView: View {
    @StateObject var formVM = FormViewModel()
    // Objet créé une fois par durée de vie de la vue
}

Erreur : transmettre @StateObject là où @ObservedObject est nécessaire

Si une View enfant déclare le même ObservableObject via @StateObject, elle crée une copie indépendante. Les changements dans l’objet parent ne seront pas visibles dans l’enfant, et vice versa. Utilisez toujours @ObservedObject pour les Views enfants qui reçoivent l’objet de l’extérieur.

Alternatives à @ObservedObject dans SwiftUI

Dans SwiftUI moderne, il existe plusieurs alternatives à @ObservedObject, chacune avec ses avantages. Le choix dépend de l’architecture de l’application, de la version d’iOS et du cas d’utilisation spécifique.

  • @EnvironmentObject — permet d’obtenir un objet de l’environnement SwiftUI sans le passer explicitement via l’initialiseur. Pratique pour les objets nécessaires à plusieurs écrans, mais nécessite une injection explicite via .environmentObject().
  • @State + @Binding — pour les types de valeur simples, ObservableObject n’est pas nécessaire. Utilisez @State pour le stockage et @Binding pour la transmission aux Views enfants.
  • @AppStorage — pour les valeurs UserDefaults qui doivent se synchroniser automatiquement avec la View.
  • @SceneStorage — pour conserver l’état temporaire entre les redémarrages de scène (par exemple, position de défilement dans une liste).

Le choix entre @ObservedObject et @EnvironmentObject est une question de style et d’architecture. @ObservedObject montre explicitement les dépendances de la View via l’initialiseur, rendant le code plus prévisible. @EnvironmentObject est pratique pour les hiérarchies profondes mais cache les dépendances, ce qui peut compliquer le débogage.

Questions fréquentes

Peut-on utiliser @ObservedObject sans @StateObject dans le parent ?

Oui, si l’objet est créé et stocké en dehors de SwiftUI — par exemple, dans un AppDelegate ou un singleton. Dans ce cas, @ObservedObject s’abonne simplement aux changements d’un objet existant. Cependant, pour les objets créés dans la hiérarchie SwiftUI, @StateObject est toujours nécessaire quelque part au-dessus.

Pourquoi @ObservedObject ne met-il parfois pas à jour la View ?

La raison la plus probable est que la propriété est modifiée pas via @Published ou que l’objet lui-même n’est pas modifié mais sa structure interne mute sans appeler objectWillChange. Pour les collections, utilisez l’affectation d’une nouvelle copie : array.append() ne suffit pas — vous devez réaffecter le tableau lui-même via array = array + [element].

@ObservedObject affecte-t-il les performances ?

@ObservedObject lui-même ne crée pas de surcharge significative. Les problèmes surviennent avec les changements fréquents de propriétés @Published — chaque changement déclenche un redessin de toutes les Views observatrices. Pour optimiser, utilisez EquatableView, réduisez le nombre de propriétés publiées et évitez les mises à jour inutiles.

Quelle est la différence entre @ObservedObject et @Binding ?

@ObservedObject observe une classe ObservableObject entière et redessine la View lors de tout changement de ses propriétés publiées. @Binding crée une connexion bidirectionnelle avec une valeur spécifique (String, Int, Bool) et permet de la lire et de l’écrire. @Binding est plus léger et ne nécessite pas ObservableObject.

Peut-on combiner @ObservedObject avec @Published dans la même classe ?

Oui, c’est le motif standard. @Published dans ObservableObject s’intègre automatiquement avec @ObservedObject. Chaque propriété @Published ajoute un observateur au publisher objectWillChange. Lorsque l’une d’elles change, toutes les Views avec @ObservedObject pour cet objet sont redessinées.

Résumé

  • @ObservedObject — un property wrapper pour observer un ObservableObject créé ailleurs dans la hiérarchie.
  • Ne possède pas l’objet — contrairement à @StateObject, @ObservedObject ne gère pas le cycle de vie et ne crée pas l’objet.
  • Abonnement via Combine — SwiftUI s’abonne automatiquement au publisher objectWillChange de l’ObservableObject.
  • iOS 13+ — @ObservedObject est disponible depuis la première version de SwiftUI, important pour les projets avec support hérité.
  • Transmis via initialiseur — l’objet est explicitement transmis à la View enfant, rendant les dépendances transparentes.
  • Erreur de propriété — utiliser @ObservedObject pour créer un objet entraîne une perte de données lors de la reconstruction de la View.
  • Alternatives — @EnvironmentObject pour l’injection par environnement, @State/@Binding pour les types de valeur.

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.

Discuter du projet

Lisez aussi