@StateObject: wat is het, verschil met @ObservedObject en voorbeelden

Auteur: IT Sectr Gepubliceerd: 2026-06-19 Leestijd: 7 min

@StateObject is een Property Wrapper in SwiftUI voor het maken en bezitten van een ObservableObject-instantie direct in de view. SwiftUI garandeert dat het object eenmalig wordt geïnitialiseerd gedurende de levenscyclus van de weergave en niet opnieuw wordt aangemaakt bij herhaalde renders. Volgens Apple Developer Documentation (2025) wordt @StateObject aanbevolen voor root-views die een gegevensbron creëren. @StateObject is de juiste keuze voor het bezitten van ObservableObject in de SwiftUI-hiërarchie.

Belangrijkste punten

  • @StateObject — Property Wrapper voor het maken en bezitten van ObservableObject in een view
  • Enkele instantie — het object wordt eenmaal gemaakt en niet opnieuw bij renders
  • Bron van waarheid — @StateObject garandeert gegevensstabiliteit voor de hele hiërarchie
  • Verschil met @ObservedObject — @ObservedObject bezit het object niet en kan het verliezen
  • Root-views — @StateObject wordt gebruikt in de view die het object maakt

Wat is @StateObject in SwiftUI?

@StateObject is een Property Wrapper die verscheen in SwiftUI 2.0 (iOS 14) en de mogelijkheden van @ObservedObject en @State combineert. Net als @ObservedObject abonneert het zich op wijzigingen van ObservableObject. Net als @State garandeert het dat gegevens meerdere initialisaties van de view-structuur overleven. @StateObject maakt het object eenmalig aan bij de eerste verschijning van de view op het scherm en slaat het op in de SwiftUI-heap.

Vóór de komst van @StateObject gebruikten ontwikkelaars @ObservedObject voor alle ObservableObjecten, inclusief die in views werden gemaakt. Dit leidde tot frequent gegevensverlies bij het bijwerken van de bovenliggende view, wanneer de view-structuur opnieuw werd aangemaakt en de @ObservedObject-instantie meenam. @StateObject loste dit probleem op door een stabiliteitsgarantie toe te voegen.

De hoofdregel: @StateObject wordt toegepast in de view die het object maakt in de standaard initialisator (let model = ViewModel()). Kind-views die dit object ontvangen, gebruiken @ObservedObject. Deze scheiding garandeert een enkele bron van waarheid in de hele hiërarchie.

Levenscyclus van @StateObject

SwiftUI beheert de levenscyclus van @StateObject via een opslagbeheerder, vergelijkbaar met @State. Bij de eerste verschijning van de view wijst SwiftUI geheugen toe voor het object en slaat het op in een persistent gebied. Bij herhaalde renders (body-aanroep) wordt het object niet opnieuw aangemaakt — de bestaande instantie wordt gebruikt. Het object leeft zolang de view in de hiërarchie aanwezig is.

Wanneer de view uit de hiërarchie wordt verwijderd, vernietigt SwiftUI de @StateObject en roept deinit aan. Bij het opnieuw toevoegen van de view aan de hiërarchie wordt een nieuwe instantie gemaakt. Dit is belangrijk om te overwegen bij het ontwerpen: als u gegevens wilt bewaren tussen verwijderingen van views, gebruik dan een servicelaag (singleton of DI) of @AppStorage voor persistentie.

swift
class TimerViewModel: ObservableObject {
    @Published var seconds: Int = 0
    private var timer: Timer?

    func start() {
        timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in
            self.seconds += 1
        }
    }

    deinit {
        timer?.invalidate()
    }
}

struct TimerView: View {
    @StateObject var viewModel = TimerViewModel()

    var body: some View {
        Text("\(viewModel.seconds)s")
            .onAppear { viewModel.start() }
    }
}

In het voorbeeld wordt TimerViewModel gemaakt via @StateObject en leeft zolang TimerView op het scherm staat. De timer start in onAppear en stopt in deinit. Als @ObservedObject was gebruikt, zou bij elke render van TimerView een nieuwe TimerViewModel met seconden = 0 worden gemaakt en zou de timer nooit correct werken. @StateObject garandeert dat viewModel uniek en stabiel is.

@StateObject vs @ObservedObject: vergelijking

De keuze tussen @StateObject en @ObservedObject hangt af van wie het object bezit. Als de view het object maakt — @StateObject. Als de view een kant-en-klaar object ontvangt — @ObservedObject. Deze regel is zo belangrijk dat Xcode een waarschuwing geeft bij het gebruik van @StateObject in een kind-view dat het object via een initialisator ontvangt.

SituatieAanbevolen wrapper
View maakt model via ViewModel()@StateObject
View ontvangt model van ouder@ObservedObject
Model wordt in één view gebruikt@StateObject
Model wordt via Environment doorgegeven@EnvironmentObject
Model is nodig voor voorbeeld@ObservedObject + mock

In de praktijk wordt bij de start van een project vaak @StateObject in de root-view gebruikt en @ObservedObject in alle kind-views. Naarmate de applicatie groeit, kan een deel van @StateObject worden vervangen door @EnvironmentObject om de hiërarchie te vereenvoudigen. @StateObject blijft echter de beste keuze voor modulaire schermen met eigen logica.

Patronen voor @StateObject-gebruik

Het eerste patroon — MVVM met @StateObject. ViewModel als ObservableObject wordt in de view gemaakt via @StateObject. ViewModel bevat @Published-eigenschappen en bedrijfslogica. De view abonneert zich op wijzigingen en werkt de interface bij. Deze benadering biedt testbare isolatie: ViewModel kan zonder UI worden getest door direct een instantie te maken.

Het tweede patroon — @StateObject met afhankelijkheden. Als ViewModel services nodig heeft, gebruik dan initialisatie met parameters. Bijvoorbeeld @StateObject var viewModel = UserViewModel(api: APIClient.shared). Wees echter voorzichtig: parameters worden bij elke body-render berekend, maar het object wordt slechts eenmaal gemaakt. SwiftUI negeert volgende initialisaties van @StateObject.

Het derde patroon — geneste @StateObject. In SwiftUI kun je meerdere @StateObject in één view hebben, maar dit is zelden gerechtvaardigd. Meestal is één @StateObject verantwoordelijk voor de volledige gegevensset van de view. Als de logica te complex wordt, splits deze dan op in een compositie van @ObservedObject-services binnen één @StateObject.

swift
struct AppView: View {
    @StateObject var router = NavigationRouter()
    @StateObject var auth = AuthViewModel()

    var body: some View {
        ContentView()
            .environmentObject(router)
            .environmentObject(auth)
    }
}

In het voorbeeld maakt AppView twee @StateObject aan: NavigationRouter voor navigatiebeheer en AuthViewModel voor authenticatie. Beide objecten worden via environmentObject in Environment geïnjecteerd. Elke kind-view heeft er toegang toe via @EnvironmentObject zonder ze door de keten van initialisatoren te moeten geven.

@StateObject en initialisatie met parameters

@StateObject ondersteunt initialisatie met elke parameters, maar met een belangrijke eigenschap: de initialisator wordt slechts eenmaal aangeroepen. Bij herhaalde body-renders wordt de nieuwe waarde van parameters genegeerd. Dit betekent dat als u @State var id: Int = 5 doorgeeft aan @StateObject var vm = ViewModel(id: id), ViewModel bij wijziging van id de nieuwe waarde niet ontvangt.

Gebruik onReceive of onAppear voor synchronisatie om dit probleem op te lossen. Abonneer u op parameterwijzigingen in ViewModel via Combine of geef parameters door via de .onChange(of:)-methode op view-niveau. Een alternatief is het gebruik van @ObservedObject in plaats van @StateObject als het object dynamisch moet reageren op externe wijzigingen.

swift
struct DetailView: View {
    let itemId: Int
    @StateObject var viewModel = DetailViewModel()

    var body: some View {
        Text(viewModel.title)
            .onAppear { viewModel.load(id: itemId) }
    }
}

De juiste aanpak: DetailView ontvangt itemId als let-eigenschap (doorgegeven via de struct-initialisator) en @StateObject maakt DetailViewModel zonder parameters. In onAppear wordt de methode load(id:) aangeroepen, die gegevens laadt voor de doorgegeven ID. Dit garandeert dat ViewModel is gemaakt door het @StateObject-mechanisme, maar gegevens worden bij elke verschijning van de view met de huidige ID geladen.

Veelvoorkomende fouten met @StateObject

De belangrijkste fout — het gebruik van @StateObject in kind-views die het object van de ouder ontvangen. Als ParentView @StateObject model maakt en ChildView declareert @StateObject var model: ModelType (met standaardparameter), dan zal ChildView zijn eigen onafhankelijke instantie maken. De ouder- en kind-objecten zijn niet gekoppeld en wijzigingen in de ene worden niet weerspiegeld in de andere.

De tweede fout — @StateObject in List of ForEach plaatsen. Elk element van de lijst maakt zijn eigen @StateObject aan, wat leidt tot meerdere onafhankelijke instanties. Voor lijsten is het correct om één ObservableObject aan alle elementen door te geven via @ObservedObject of Identifiable-structuren met @State in List te gebruiken.

Het derde probleem — gebrek aan opschoning in deinit. @StateObject leeft de hele levenscyclus van de view. Als het object timers, Combine-abonnementen of netwerkverzoeken maakt, moet deinit deze annuleren. Anders zijn geheugenlekken en het voortzetten van achtergrondwerk na het sluiten van het scherm onvermijdelijk. Gebruik altijd Combine Cancellable store of invalidate timers in deinit.

Veelgestelde vragen

Wanneer verscheen @StateObject in SwiftUI?

@StateObject is toegevoegd in SwiftUI 2.0 op WWDC 2020 samen met iOS 14, macOS 11, watchOS 7 en tvOS 14. Daarvoor was @ObservedObject de enige manier om met ObservableObject te werken, wat leidde tot frequente bugs met gegevensverlies.

Kan @StateObject optioneel zijn?

Nee, @StateObject ondersteunt geen Optional-typen. Het object moet bij declaratie worden geïnitialiseerd. Als u een optioneel object nodig hebt, gebruik dan @ObservedObject of @EnvironmentObject met een optioneel type.

Hoe controleer ik of @StateObject slechts één keer is gemaakt?

Voeg print(#function) toe in de initialisator en deinit van ObservableObject. Als init bij herhaalde renders niet wordt aangeroepen — werkt @StateObject correct. Als init elke keer wordt aangeroepen — vervang @ObservedObject door @StateObject.

Kan ik @StateObject met UIKit gebruiken via UIHostingController?

Ja, @StateObject werkt in SwiftUI-views die via UIHostingController in UIKit zijn ingebed. De levenscyclus van het object is gekoppeld aan de SwiftUI-view, niet aan UIViewController. Als de SwiftUI-view wordt vervangen, wordt @StateObject vernietigd.

Wat is beter: één @StateObject met grote ViewModel of meerdere kleine?

Meerdere kleine @StateObject met gescheiden verantwoordelijkheden. Dit verbetert testbaarheid, herbruikbaarheid en prestaties — bij wijziging van één object worden alleen de geabonneerde delen van de interface opnieuw getekend, niet de hele view.

Samenvatting

  • @StateObject — Property Wrapper voor het maken en bezitten van ObservableObject in een view
  • Enkele instantie — het object wordt niet opnieuw gemaakt bij herhaalde body-renders
  • Bron van waarheid — @StateObject in de root-view garandeert gegevensstabiliteit voor de hiërarchie
  • Kiesregel — @StateObject voor maken, @ObservedObject voor ontvangen van kant-en-klaar object
  • Initialisatie — parameters in @StateObject worden eenmaal berekend, updates worden niet gevolgd
  • Deinit — verplichte opschoning van timers en abonnementen in deinit van ObservableObject
  • iOS 14+ — @StateObject beschikbaar vanaf iOS 14, macOS 11, watchOS 7, tvOS 14

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.

Bespreek het project

Lees ook