@StateObject : ce que c'est, création et gestion d'ObservableObject

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

@StateObject est un property wrapper dans SwiftUI qui crée et possède une instance d'ObservableObject tout au long du cycle de vie d'une View. Lorsqu'une View apparaît pour la première fois à l'écran, @StateObject initialise l'objet et le stocke jusqu'à ce que la View soit supprimée de la mémoire. Cela garantit que les données ne sont pas réinitialisées lors de la reconstruction de l'interface — par exemple, lors du changement de thème ou de la mise à jour de la View parente. Selon la Documentation Développeur Apple (2025), @StateObject doit être utilisé comme source principale de vérité (source of truth) pour ObservableObject dans la hiérarchie SwiftUI, tandis que les Views enfants reçoivent l'objet déjà créé via @ObservedObject ou @EnvironmentObject.

Points Clés

  • @StateObject est un property wrapper pour créer et posséder un ObservableObject à l'intérieur d'une View.
  • Création unique — l'objet est initialisé une fois pendant la durée de vie de la View et n'est pas recréé lors des reconstructions.
  • Source de vérité — @StateObject est la source de vérité dans la hiérarchie, contrairement à @ObservedObject.
  • Cycle de vie — l'objet vit tant que la View existe en mémoire et est détruit avec elle.
  • Initialisation — @StateObject nécessite une valeur initiale à la création, généralement via init avec paramètres.

Qu'est-ce que @StateObject dans SwiftUI

@StateObject est un property wrapper introduit dans iOS 14 qui permet à une View de créer et de posséder une instance d'une classe conforme au protocole ObservableObject. Contrairement à @State qui fonctionne avec des types valeur (structs), @StateObject est conçu pour les types référence — des classes qui peuvent notifier SwiftUI des modifications de leurs propriétés.

Lorsqu'une View utilise @StateObject var viewModel: MyViewModel, SwiftUI crée automatiquement une instance de MyViewModel lors de la première apparition de la View et la stocke dans un stockage spécial du framework. À chaque mise à jour de la View (par exemple, lorsque l'état parent change), SwiftUI ne recrée pas l'objet — il utilise l'instance existante jusqu'à ce que la View soit supprimée de la hiérarchie.

Selon Apple WWDC Session 10137 (2024), @StateObject résout le problème de perte de données qui existait dans iOS 13 lors de la reconstruction des Views, forçant les développeurs à créer ObservableObject dans la View parente et à le passer via l'initialiseur. Cela entraînait une duplication de code et le risque de recréer accidentellement l'objet.

swift
import SwiftUI

class CounterViewModel: ObservableObject {
    @Published var count: Int = 0
    
    func increment() {
        count += 1
    }
}

struct CounterView: View {
    @StateObject var viewModel = CounterViewModel()
    
    var body: some View {
        VStack {
            Text("Count: \(viewModel.count)")
            Button("Increment", action: viewModel.increment)
        }
    }
}

Comment fonctionne @StateObject

Le mécanisme de @StateObject est basé sur l'intégration de SwiftUI avec le framework Combine. Lorsqu'un ObservableObject marque ses propriétés avec l'attribut @Published, SwiftUI s'abonne automatiquement aux changements via le publisher intégré au protocole ObservableObject. Lorsqu'une propriété publiée change, l'objet envoie un signal via le publisher objectWillChange, ce qui déclenche un redessin de toutes les Views qui observent cet objet.

SwiftUI stocke l'instance d'ObservableObject dans un stockage spécial lié à une instance spécifique de View. Ce stockage est créé une fois lors du premier rendu et existe jusqu'à ce que la View soit détruite. C'est pourquoi @StateObject garantit la stabilité de la référence — SwiftUI gère la mémoire automatiquement, sans dépendre de l'initialiseur de la View.

Selon objc.io — Thinking in SwiftUI (2025), l'implémentation interne de @StateObject utilise un mécanisme similaire à @State mais pour les types référence : SwiftUI crée une enveloppe (boxing) autour de l'objet et gère son cycle de vie via son propre allocateur, optimisé pour les reconstructions fréquentes de la hiérarchie des Views.

Cycle de vie de @StateObject

  • Création — lorsque la View apparaît pour la première fois à l'écran, SwiftUI appelle l'initialiseur de l'objet et stocke la référence.
  • Reconstruction — lorsque la View parente est mise à jour, l'objet n'est pas recréé ; l'instance existante est utilisée.
  • Destruction — lorsque la View quitte l'écran et est supprimée de la hiérarchie, SwiftUI appelle le deinit de l'objet.

@StateObject vs @ObservedObject : différences clés

La principale différence entre @StateObject et @ObservedObject réside dans la propriété de l'objet. @StateObject crée et stocke l'objet — il en est le propriétaire. @ObservedObject observe seulement l'objet qui a été créé ailleurs et passé via l'initialiseur ou une propriété.

Caractéristique@StateObject@ObservedObject
PropriétéCrée et possède l'objetObserve seulement
InitialisationÀ l'intérieur de la View via init/défautExterne, passée via paramètre
Cycle de vieLié au cycle de vie de la ViewNon contrôlé par la View
RecréationN'est pas recréé lors de la mise à jourPeut être remplacé extérieurement
Version iOSiOS 14+iOS 13+

La règle est simple : si la View crée l'ObservableObject — utilisez @StateObject. Si la View reçoit seulement un objet déjà créé du parent — utilisez @ObservedObject. Violer cette règle entraîne soit une perte de données (si @ObservedObject est utilisé pour la propriété), soit une création excessive d'objets (si @StateObject est utilisé pour l'observation).

Quand utiliser @StateObject

@StateObject doit être utilisé dans les Views qui sont la source de vérité pour un ensemble spécifique de données. Les scénarios typiques incluent les écrans avec leur propre view model, les écrans racines des piles de navigation et les présentations modales gérant leur propre état.

  • Écran avec view model — chaque écran qui gère ses propres données et logique doit créer son view model via @StateObject.
  • View racine — dans une hiérarchie NavigationStack ou TabView, l'élément racine crée les données, et les éléments enfants les reçoivent via @ObservedObject.
  • Fenêtres modales — .sheet et .fullScreenCover nécessitent souvent leur propre @StateObject pour gérer un formulaire ou un processus.
  • Liste modifiable — chaque ligne de liste contenant un formulaire d'édition doit avoir son propre @StateObject.
swift
struct ProfileView: View {
    @StateObject var viewModel = ProfileViewModel()
    
    var body: some View {
        NavigationStack {
            Form {
                TextField("Name", text: $viewModel.name)
                TextField("Email", text: $viewModel.email)
                Button("Save") {
                    viewModel.saveProfile()
                }
            }
            .navigationTitle("Profile")
        }
    }
}

Initialiser @StateObject avec des paramètres

Initialiser @StateObject avec des paramètres nécessite une syntaxe spéciale, car SwiftUI gère lui-même la création de l'objet. Vous ne pouvez pas simplement passer des paramètres à l'initialiseur — vous devez utiliser une closure échappée ou une méthode d'usine séparée.

Selon Swift by Sundell (2024), l'approche la plus propre est d'utiliser une méthode d'usine ou une closure que SwiftUI appellera lors de la première création de l'objet. Une approche alternative consiste à initialiser l'ObservableObject dans la View parente et à le passer via @StateObject en utilisant l'initialiseur standard.

swift
class UserViewModel: ObservableObject {
    @Published var user: User
    
    init(user: User) {
        self.user = user
    }
}

struct UserDetailView: View {
    @StateObject var viewModel: UserViewModel
    
    init(user: User) {
        _viewModel = StateObject(wrappedValue: UserViewModel(user: user))
    }
    
    var body: some View {
        Text(viewModel.user.name)
    }
}

Il est important de se rappeler que l'initialiseur de View avec @StateObject doit utiliser un underscore avant le nom de la propriété (_viewModel) pour accéder au property wrapper lui-même, et non à sa valeur. C'est un modèle Swift standard pour travailler avec les property wrappers dans les initialiseurs.

Erreurs courantes avec @StateObject

L'erreur la plus courante est d'utiliser @ObservedObject au lieu de @StateObject pour une View qui devrait posséder l'objet. Dans ce cas, chaque fois que le parent est reconstruit, l'objet sera recréé, entraînant la perte de toutes les données accumulées. Cette erreur est particulièrement insidieuse dans les hiérarchies complexes avec NavigationStack ou TabView.

  • Perte de données lors de la navigation — si un écran enfant utilise @ObservedObject pour son propre view model, les données seront réinitialisées lors du retour en arrière et de la réouverture.
  • Fuite de mémoire — créer @StateObject dans une View parente qui n'est jamais supprimée peut entraîner une accumulation d'objets si chaque écran enfant crée également @StateObject sans contrôle.
  • Duplication d'objets — passer un seul ObservableObject à plusieurs @StateObject dans différentes Views crée plusieurs instances indépendantes qui ne se synchronisent pas entre elles.

Pour éviter ces problèmes, suivez une règle simple : un @StateObject par source de vérité. Si les données doivent être partagées entre plusieurs écrans — créez @StateObject une fois dans la View racine et passez-le via @ObservedObject ou @EnvironmentObject aux éléments enfants.

swift
// ❌ Wrong: @ObservedObject for owning an object
struct BadView: View {
    @ObservedObject var vm = ViewModel() // will be recreated on each update!
}

// ✅ Correct: @StateObject for owning
struct GoodView: View {
    @StateObject var vm = ViewModel() // created once for View lifetime
}

Foire Aux Questions

Quelle est la différence entre @StateObject et @State ?

@State fonctionne avec les types valeur (structs, chaînes, nombres) et stocke la valeur directement dans le stockage SwiftUI. @StateObject fonctionne avec les types référence — les classes conformes à ObservableObject. @State convient pour les états locaux simples, @StateObject pour les objets complexes avec logique et propriétés publiées.

Puis-je utiliser @StateObject dans iOS 13 ?

Non, @StateObject n'est disponible qu'à partir d'iOS 14. Pour iOS 13, utilisez @ObservedObject et créez l'ObservableObject dans la View parente via @State avec une gestion manuelle du cycle de vie. Une alternative est d'utiliser @State avec un struct au lieu d'une classe pour les données qui ne nécessitent pas de sémantique de référence.

Que se passe-t-il si j'utilise @StateObject dans une View enfant où l'objet est passé depuis le parent ?

La View enfant créera sa propre copie de l'ObservableObject, complètement indépendante de celle du parent. Les modifications dans l'une n'affecteront pas l'autre. C'est presque toujours une erreur : utilisez @ObservedObject pour recevoir un objet du parent et @StateObject uniquement pour créer un nouvel objet à l'intérieur de la View.

Quand un objet créé via @StateObject est-il détruit ?

L'objet est détruit lorsque la View qui l'a créé est complètement supprimée de la hiérarchie SwiftUI. Pour un écran dans NavigationStack, cela se produit lors du pop de la pile de navigation. Pour une fenêtre modale — lorsqu'elle est fermée. Pour TabView — lors du changement d'onglet, si la View n'est pas mise en cache.

Comment passer des paramètres à @StateObject lors de l'initialisation ?

Utilisez un init personnalisé avec un accès au property wrapper via underscore : _viewModel = StateObject(wrappedValue: MyViewModel(param: value)). Ce modèle permet de passer n'importe quels paramètres à l'ObservableObject tout en conservant la garantie de création unique de l'objet pendant la durée de vie de la View.

Résumé

  • @StateObject — un property wrapper pour créer et posséder un ObservableObject à l'intérieur d'une View, disponible depuis iOS 14.
  • Garantie de création unique — l'objet est initialisé une fois et n'est pas recréé lors de la reconstruction de la View.
  • Source de vérité — @StateObject est la source de vérité, tandis que @ObservedObject n'est qu'un observateur.
  • Cycle de vie — l'objet vit tant que la View existe dans la hiérarchie SwiftUI et est détruit lorsqu'il la quitte.
  • Initialisation avec paramètres — nécessite l'accès au property wrapper via _viewModel et StateObject(wrappedValue:).
  • Erreur de propriété — utiliser @ObservedObject pour créer un objet entraîne une perte de données lors de la reconstruction.
  • Un objet — un @StateObject — pour les données partagées, créez @StateObject dans la View racine et passez-le aux enfants via @ObservedObject.

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