@StateObject är en Property Wrapper i SwiftUI för att skapa och äga en ObservableObject-instans direkt i vyn. SwiftUI garanterar att objektet initieras en gång under vyens livscykel och inte återskapas vid upprepade renderingar. Enligt Apple Developer Documentation (2025) rekommenderas @StateObject för rotvyer som skapar en datakälla. @StateObject är rätt val för att äga ObservableObject i SwiftUI-hierarkin.
Huvudpunkter
@StateObject är en Property Wrapper som dök upp i SwiftUI 2.0 (iOS 14) och kombinerar funktionerna hos @ObservedObject och @State. Precis som @ObservedObject prenumererar den på ändringar av ObservableObject. Precis som @State garanterar den att data överlever upprepade initieringar av vy-strukturen. @StateObject skapar objektet en gång vid vyens första visning på skärmen och lagrar det på SwiftUI-heapen.
Före tillkomsten av @StateObject använde utvecklare @ObservedObject för alla ObservableObject, inklusive de som skapades i vyer. Detta ledde till frekvent dataförlust vid uppdatering av den överordnade vyn, när vy-strukturen återskapades och tog med sig @ObservedObject-instansen. @StateObject löste detta problem genom att lägga till en stabilitetsgaranti.
Huvudregeln: @StateObject tillämpas i vyn som skapar objektet i standardinitieraren (let model = ViewModel()). Barnvyer som tar emot detta objekt använder @ObservedObject. En sådan uppdelning garanterar en enda sanningskälla i hela hierarkin.
SwiftUI hanterar livscykeln för @StateObject genom en lagringshanterare, liknande @State. Vid vyens första visning allokerar SwiftUI minne för objektet och sparar det i ett beständigt område. Vid upprepade renderingar (anrop av body) återskapas inte objektet — den befintliga instansen används. Objektet lever så länge vyn finns i hierarkin.
När vyn tas bort från hierarkin förstör SwiftUI @StateObject och anropar deinit. När vyn läggs tillbaka i hierarkin skapas en ny instans. Detta är viktigt att ta hänsyn till vid design: om du behöver bevara data mellan borttagningar av vyer, använd en tjänstelager (singleton eller DI) eller @AppStorage för beständighet.
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() }
}
}
I exemplet skapas TimerViewModel via @StateObject och lever så länge TimerView är på skärmen. Timern startar i onAppear och stoppas i deinit. Om @ObservedObject hade använts skulle varje rendering av TimerView skapa en ny TimerViewModel med sekunder = 0 och timern skulle aldrig fungera korrekt. @StateObject garanterar att viewModel är unik och stabil.
Valet mellan @StateObject och @ObservedObject beror på vem som äger objektet. Om vyn skapar objektet — @StateObject. Om vyn tar emot ett färdigt objekt — @ObservedObject. Denna regel är så viktig att Xcode utfärdar en varning vid användning av @StateObject i en barnvy som tar emot objektet via en initierare.
| Situation | Rekommenderad wrapper |
|---|---|
| Vyn skapar modell via ViewModel() | @StateObject |
| Vyn tar emot modell från förälder | @ObservedObject |
| Modell används i en vy | @StateObject |
| Modell skickas via Environment | @EnvironmentObject |
| Modell behövs för förhandsvisning | @ObservedObject + mock |
I praktiken används ofta @StateObject i rotvyn och @ObservedObject i alla barnvyer i början av ett projekt. När applikationen växer kan en del av @StateObject ersättas med @EnvironmentObject för att förenkla hierarkin. @StateObject förblir dock det bästa valet för modulära skärmar med egen logik.
Det första mönstret — MVVM med @StateObject. ViewModel som ObservableObject skapas i vyn via @StateObject. ViewModel innehåller @Published-egenskaper och affärslogik. Vyn prenumererar på ändringar och uppdaterar gränssnittet. Detta tillvägagångssätt ger testbar isolering: ViewModel kan testas utan UI genom att skapa en instans direkt.
Det andra mönstret — @StateObject med beroenden. Om ViewModel kräver tjänster, använd initiering med parametrar. Till exempel @StateObject var viewModel = UserViewModel(api: APIClient.shared). Var dock försiktig: parametrar beräknas vid varje body-rendering, men objektet skapas bara en gång. SwiftUI ignorerar efterföljande initieringar av @StateObject.
Det tredje mönstret — nästlade @StateObject. I SwiftUI kan du ha flera @StateObject i en vy, men det är sällan motiverat. Vanligtvis ansvarar en @StateObject för hela vyens datamängd. Om logiken blir för komplex, dela upp den i en sammansättning av @ObservedObject-tjänster inom en @StateObject.
struct AppView: View {
@StateObject var router = NavigationRouter()
@StateObject var auth = AuthViewModel()
var body: some View {
ContentView()
.environmentObject(router)
.environmentObject(auth)
}
}
I exemplet skapar AppView två @StateObject: NavigationRouter för navigeringshantering och AuthViewModel för autentisering. Båda objekten injiceras i Environment via environmentObject. Vilken barnvy som helst kan komma åt dem via @EnvironmentObject utan att behöva skicka dem genom initierarkedjan.
@StateObject stöder initiering med valfria parametrar, men med en viktig egenskap: initieraren anropas endast en gång. Vid upprepade body-renderingar ignoreras det nya värdet på parametrarna. Detta innebär att om du skickar @State var id: Int = 5 till @StateObject var vm = ViewModel(id: id), kommer ViewModel inte att få det nya värdet när id ändras.
För att lösa detta problem, använd onReceive eller onAppear för synkronisering. Prenumerera på parameterändringar inuti ViewModel via Combine eller skicka parametrar via .onChange(of:)-metoden på vy-nivå. Alternativ — använd @ObservedObject istället för @StateObject om objektet måste reagera dynamiskt på externa ändringar.
struct DetailView: View {
let itemId: Int
@StateObject var viewModel = DetailViewModel()
var body: some View {
Text(viewModel.title)
.onAppear { viewModel.load(id: itemId) }
}
}
Rätt tillvägagångssätt: DetailView tar emot itemId som en let-egenskap (skickad via strukturens initierare), och @StateObject skapar DetailViewModel utan parametrar. I onAppear anropas metoden load(id:), som laddar data för det skickade ID:t. Detta garanterar att ViewModel skapas av @StateObject-mekanismen, men data laddas vid varje vyförekomst med aktuellt ID.
Huvudmisstaget — att använda @StateObject i barnvyer som tar emot objektet från föräldern. Om ParentView skapar @StateObject model och ChildView deklarerar @StateObject var model: ModelType (med standardparameter), kommer ChildView att skapa sin egen oberoende instans. Förälderns och barnets objekt kommer inte att vara kopplade och ändringar i det ena kommer inte att återspeglas i det andra.
Det andra misstaget — att placera @StateObject i List eller ForEach. Varje element i listan skapar sin egen @StateObject, vilket leder till flera oberoende instanser. För listor är det korrekt att skicka ett ObservableObject till alla element via @ObservedObject eller använda Identifiable-strukturer med @State inuti List.
Det tredje problemet — brist på rensning i deinit. @StateObject lever under vyens hela livscykel. Om objektet skapar timer, Combine-prenumerationer eller nätverksförfrågningar måste deinit annullera dem. Annars är minnesläckor och fortsatt bakgrundsarbete efter att skärmen stängts oundvikliga. Använd alltid Combine Cancellable store eller invalidate av timer i deinit.
Vanliga frågor
@StateObject lades till i SwiftUI 2.0 på WWDC 2020 tillsammans med iOS 14, macOS 11, watchOS 7 och tvOS 14. Innan dess var @ObservedObject det enda sättet att arbeta med ObservableObject, vilket ledde till frekenta buggar med dataförlust.
Nej, @StateObject stöder inte Optional-typer. Objektet måste initieras vid deklaration. Om du behöver ett valfritt objekt, använd @ObservedObject eller @EnvironmentObject med en valfri typ.
Lägg till print(#function) i initieraren och deinit för ObservableObject. Om init inte anropas vid upprepade renderingar — fungerar @StateObject korrekt. Om init anropas varje gång — ersätt @ObservedObject med @StateObject.
Ja, @StateObject fungerar i SwiftUI-vyer inbäddade i UIKit via UIHostingController. Objektets livscykel är bunden till SwiftUI-vyn, inte till UIViewController. Om SwiftUI-vyn ersätts förstörs @StateObject.
Flera små @StateObject med separerade ansvarsområden. Detta förbättrar testbarhet, återanvändbarhet och prestanda — när ett objekt ändras ritas endast de prenumererade delarna av gränssnittet om, inte hela vyn.
Sammanfattning
Vi utvecklar en mobil applikation nyckelfärdigt
IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.
Läs också