@StateObject is een property wrapper in SwiftUI die een ObservableObject-instantie aanmaakt en bezit gedurende de volledige levenscyclus van een View. Wanneer een View voor het eerst op het scherm verschijnt, initialiseert @StateObject het object en bewaart het totdat de View uit het geheugen wordt verwijderd. Dit garandeert dat gegevens niet worden gereset bij herbouw van de interface — bijvoorbeeld bij themawissel of bijwerken van de bovenliggende View. Volgens Apple Developer Documentation (2025) moet @StateObject worden gebruikt als de belangrijkste bron van waarheid (source of truth) voor ObservableObject in de SwiftUI-hiërarchie, terwijl onderliggende Views het reeds aangemaakte object ontvangen via @ObservedObject of @EnvironmentObject.
Belangrijkste punten
@StateObject is een property wrapper geïntroduceerd in iOS 14 die een View in staat stelt een instantie van een klasse die voldoet aan het ObservableObject-protocol aan te maken en te bezitten. In tegenstelling tot @State, dat werkt met waardetypen (structuren), is @StateObject bedoeld voor referentietypen — klassen die SwiftUI op de hoogte kunnen stellen van wijzigingen in hun eigenschappen.
Wanneer een View @StateObject var viewModel: MyViewModel gebruikt, maakt SwiftUI automatisch een MyViewModel-instantie aan bij de eerste weergave van de View en slaat deze op in een speciale opslag van het framework. Bij elke update van de View (bijvoorbeeld bij wijziging van de bovenliggende toestand) maakt SwiftUI het object niet opnieuw aan — het gebruikt de bestaande instantie totdat de View uit de hiërarchie wordt verwijderd.
Volgens Apple WWDC Session 10137 (2024) lost @StateObject het probleem van gegevensverlies bij herbouw van de View op dat in iOS 13 bestond, toen ontwikkelaars ObservableObject in de bovenliggende View moesten aanmaken en via de initialisator moesten doorgeven. Dit leidde tot code-duplicatie en risico op onbedoelde herbouw van het object.
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("Aantal: \(viewModel.count)")
Button("Verhogen", action: viewModel.increment)
}
}
}
Het mechanisme van @StateObject is gebaseerd op de integratie van SwiftUI met het Combine-framework. Wanneer ObservableObject zijn eigenschappen markeert met het @Published-attribuut, abonneert SwiftUI zich automatisch op wijzigingen via de ingebouwde publisher in het ObservableObject-protocol. Bij wijziging van een gepubliceerde eigenschap stuurt het object een signaal via de objectWillChange-publisher, wat het hertekenen van alle Views die dit object observeren activeert.
SwiftUI slaat de ObservableObject-instantie op in een speciale opslag die aan een specifieke View-instantie is gekoppeld. Deze opslag wordt eenmalig aangemaakt bij de eerste render en blijft bestaan tot vernietiging van de View. Daarom garandeert @StateObject de stabiliteit van de verwijzing naar het object — SwiftUI beheert het geheugen automatisch, zonder afhankelijk te zijn van de View-initialisator.
Volgens objc.io — Thinking in SwiftUI (2025) gebruikt de interne implementatie van @StateObject een mechanisme dat vergelijkbaar is met @State, maar voor referentietypen: SwiftUI maakt een boxing-wrapper rond het object en beheert de levenscyclus via een eigen allocator, geoptimaliseerd voor frequente herbouwingen van de View-hiërarchie.
Het belangrijkste verschil tussen @StateObject en @ObservedObject is wie het object bezit. @StateObject maakt het object aan en slaat het op — het is de eigenaar. @ObservedObject observeert alleen een object dat elders is aangemaakt en via de initialisator of eigenschap is doorgegeven.
| Kenmerk | @StateObject | @ObservedObject |
|---|---|---|
| Eigendom | Maakt aan en bezit het object | Observeert alleen |
| Initialisatie | Binnen de View via init/default | Van buitenaf, via parameter doorgegeven |
| Levenscyclus | Gebonden aan de levenscyclus van de View | Niet gecontroleerd door de View |
| Herbouw | Wordt niet herbouwd bij update | Kan van buitenaf worden vervangen |
| iOS-versie | iOS 14+ | iOS 13+ |
De regel is eenvoudig: als een View ObservableObject aanmaakt — gebruik @StateObject. Als een View alleen een reeds bestaand object van de ouder ontvangt — gebruik @ObservedObject. Schending van deze regel leidt ofwel tot gegevensverlies (als je @ObservedObject gebruikt voor eigendom), ofwel tot overmatige objectaanmaak (als je @StateObject gebruikt voor observatie).
@StateObject moet worden gebruikt in Views die de bron van waarheid zijn voor een specifieke gegevensset. Typische scenario's zijn schermen met een eigen view model, root-schermen van navigatiestapels en modale presentaties die hun eigen toestand beheren.
struct ProfileView: View {
@StateObject var viewModel = ProfileViewModel()
var body: some View {
NavigationStack {
Form {
TextField("Name", text: $viewModel.name)
TextField("Email", text: $viewModel.email)
Button("Opslaan") {
viewModel.saveProfile()
}
}
.navigationTitle("Profile")
}
}
}
Initialisatie van @StateObject met parameters vereist speciale syntax, omdat SwiftUI het aanmaken van het object onafhankelijk beheert. Je kunt niet eenvoudigweg parameters doorgeven aan de initialisator — je moet een escaping closure of een aparte aanmaakmethode gebruiken.
Volgens Swift by Sundell (2024) is de schoonste methode het gebruik van een fabrieksmethode of closure die SwiftUI zal aanroepen bij de eerste aanmaak van het object. Een alternatieve benadering — ObservableObject initialiseren in de bovenliggende View en doorgeven via @StateObject met de standaard initialisator.
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)
}
}
Het is belangrijk om te onthouden dat de View-initialisator met @StateObject een underscore voor de eigenschapsnaam (_viewModel) moet gebruiken om toegang te krijgen tot de property wrapper zelf, niet tot de waarde ervan. Dit is een standaard Swift-patroon voor het werken met property wrappers in initialisatoren.
De meest voorkomende fout is het gebruik van @ObservedObject in plaats van @StateObject voor een View dat het object zou moeten bezitten. In dit geval wordt het object bij elke herbouw van de ouder opnieuw aangemaakt, wat leidt tot verlies van alle verzamelde gegevens. Deze fout is bijzonder verraderlijk in complexe hiërarchieën met NavigationStack of TabView.
Om deze problemen te voorkomen, volg je de eenvoudige regel: één @StateObject per bron van waarheid. Als gegevens moeten worden gedeeld tussen meerdere schermen — maak dan @StateObject eenmalig aan in de root View en geef het door via @ObservedObject of @EnvironmentObject aan onderliggende elementen.
// ❌ Fout: @ObservedObject voor het bezitten van een object
struct BadView: View {
@ObservedObject var vm = ViewModel() // wordt bij elke update opnieuw aangemaakt!
}
// ✅ Juist: @StateObject voor het bezitten
struct GoodView: View {
@StateObject var vm = ViewModel() // eenmalig aangemaakt voor de View-levensduur
}
Veelgestelde vragen
@State werkt met waardetypen (structuren, strings, getallen) en slaat de waarde direct op in de SwiftUI-opslag. @StateObject werkt met referentietypen — klassen die voldoen aan ObservableObject. @State is geschikt voor eenvoudige lokale toestanden, @StateObject voor complexe objecten met logica en gepubliceerde eigenschappen.
Nee, @StateObject is alleen beschikbaar vanaf iOS 14 en hoger. Gebruik voor iOS 13 @ObservedObject en maak ObservableObject aan in de bovenliggende View via @State met handmatig levenscyclusbeheer. Een alternatief is het gebruik van @State met struct in plaats van class voor gegevens die geen referentiesemantiek vereisen.
De onderliggende View maakt zijn eigen kopie van ObservableObject aan, volledig onafhankelijk van de ouder. Wijzigingen in de ene hebben geen invloed op de andere. Dit is bijna altijd een fout: gebruik @ObservedObject voor het ontvangen van een object van de ouder en @StateObject alleen voor het aanmaken van een nieuw object binnen de View.
Het object wordt vernietigd wanneer de View die het heeft aangemaakt volledig uit de SwiftUI-hiërarchie wordt verwijderd. Voor een scherm in NavigationStack gebeurt dit bij pop van de navigatiestapel. Voor een modaal venster — bij het sluiten ervan. Voor TabView — bij het wisselen van tabblad, als de View niet wordt gecachet.
Gebruik een aangepaste init met toegang tot de property wrapper via underscore: _viewModel = StateObject(wrappedValue: MyViewModel(param: value)). Dit patroon maakt het mogelijk om willekeurige parameters aan ObservableObject door te geven, terwijl de garantie van eenmalige objectaanmaak gedurende de levensduur van de View behouden blijft.
Samenvatting
We ontwikkelen een mobiele applicatie turnkey
IT Sectr creëert sinds 2017 iOS- en Android-applicaties voor startups en bedrijven. We adviseren u en stellen de beste oplossing voor.
Lees ook