@StateObject — це property wrapper у SwiftUI, який створює та володіє екземпляром ObservableObject протягом усього життєвого циклу View. Коли View вперше з'являється на екрані, @StateObject ініціалізує об'єкт і зберігає його доти, доки View не буде видалено з пам'яті. Це гарантує, що дані не скинуться при перебудові інтерфейсу — наприклад, при зміні теми або оновленні батьківського View. За даними Apple Developer Documentation (2025), @StateObject має використовуватися як основне джерело істини (source of truth) для ObservableObject в ієрархії SwiftUI, тоді як дочірні View отримують уже створений об'єкт через @ObservedObject або @EnvironmentObject.
Головне
@StateObject — це property wrapper, представлений в iOS 14, який дозволяє View створювати та володіти екземпляром класу, що відповідає протоколу ObservableObject. На відміну від @State, який працює з value-типами (структурами), @StateObject призначений для reference-типів — класів, які можуть сповіщати SwiftUI про зміни своїх властивостей.
Коли View використовує @StateObject var viewModel: MyViewModel, SwiftUI автоматично створює екземпляр MyViewModel при першому відображенні View і зберігає його в спеціальному сховищі фреймворку. При кожному оновленні View (наприклад, при зміні батьківського стану) SwiftUI не перестворює об'єкт — він використовує існуючий екземпляр доти, доки View не буде видалено з ієрархії.
За даними Apple WWDC Session 10137 (2024), @StateObject вирішує проблему втрати даних, яка існувала в iOS 13 при перебудові View, коли розробникам доводилося створювати ObservableObject у батьківському View і передавати його через ініціалізатор. Це призводило до дублювання коду та ризику випадкового перестворення об'єкта.
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)
}
}
}
Механізм @StateObject ґрунтується на інтеграції SwiftUI з Combine framework. Коли ObservableObject позначає свої властивості атрибутом @Published, SwiftUI автоматично підписується на зміни через publisher, вбудований у протокол ObservableObject. При зміні опублікованої властивості об'єкт надсилає сигнал через objectWillChange publisher, що тригерить перемалювання всіх View, які спостерігають за цим об'єктом.
SwiftUI зберігає екземпляр ObservableObject у спеціальному сховищі, прив'язаному до конкретного екземпляра View. Це сховище створюється один раз при першому рендерингу та існує до знищення View. Саме тому @StateObject гарантує стабільність посилання — SwiftUI керує пам'яттю автоматично, не покладаючись на ініціалізатор View.
За даними objc.io — Thinking in SwiftUI (2025), внутрішня реалізація @StateObject використовує механізм, подібний до @State, але для reference-типів: SwiftUI створює boxing-обгортку навколо об'єкта та керує її життєвим циклом через власний алокатор, оптимізований для частих перебудов ієрархії View.
Головна відмінність @StateObject від @ObservedObject полягає в тому, хто володіє об'єктом. @StateObject створює та зберігає об'єкт — він є власником. @ObservedObject лише спостерігає за об'єктом, який був створений десь в іншому місці та переданий через ініціалізатор або властивість.
| Характеристика | @StateObject | @ObservedObject |
|---|---|---|
| Володіння | Створює та володіє об'єктом | Лише спостерігає |
| Ініціалізація | Всередині View через init/default | Ззовні, передається через параметр |
| Життєвий цикл | Прив'язаний до життєвого циклу View | Не контролюється View |
| Перестворення | Не перестворюється при оновленні | Може бути замінений ззовні |
| Версія iOS | iOS 14+ | iOS 13+ |
Правило просте: якщо View створює ObservableObject — використовуй @StateObject. Якщо View лише отримує вже готовий об'єкт від батька — використовуй @ObservedObject. Порушення цього правила призводить або до втрати даних (якщо використовувати @ObservedObject для володіння), або до надмірного створення об'єктів (якщо використовувати @StateObject для спостереження).
@StateObject слід використовувати в тих View, які є джерелом істини для певного набору даних. Типові сценарії включають екрани з власною view model, кореневі екрани навігаційних стеків і модальні представлення, що керують власним станом.
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")
}
}
}
Ініціалізація @StateObject з параметрами потребує спеціального синтаксису, оскільки SwiftUI керує створенням об'єкта самостійно. Не можна просто передати параметри в ініціалізатор — потрібно використовувати escape-замикання або окремий фабричний метод.
За даними Swift by Sundell (2024), найбільш чистий спосіб — використовувати фабричний метод або замикання, яке SwiftUI викличе при першому створенні об'єкта. Альтернативний підхід — ініціалізувати ObservableObject у батьківському View і передати його через @StateObject з використанням стандартного ініціалізатора.
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)
}
}
Важливо пам'ятати, що ініціалізатор View з @StateObject повинен використовувати підкреслення перед іменем властивості (_viewModel) для доступу до самого property wrapper, а не до його значення. Це стандартний патерн Swift для роботи з property wrappers в ініціалізаторах.
Найпоширеніша помилка — використання @ObservedObject замість @StateObject для View, яка повинна володіти об'єктом. У цьому випадку при кожній перебудові батька об'єкт буде створюватися заново, що призведе до втрати всіх накопичених даних. Ця помилка особливо підступна в складних ієрархіях з NavigationStack або TabView.
Щоб уникнути цих проблем, дотримуйся простого правила: один @StateObject на одне джерело істини. Якщо дані мають бути спільними для кількох екранів — створи @StateObject один раз у кореневому View і передавай через @ObservedObject або @EnvironmentObject дочірнім елементам.
// ❌ 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
}
Часті запитання
@State працює з value-типами (структури, рядки, числа) та зберігає значення безпосередньо у сховищі SwiftUI. @StateObject працює з reference-типами — класами, що відповідають ObservableObject. @State підходить для простих локальних станів, @StateObject — для складних об'єктів з логікою та опублікованими властивостями.
Ні, @StateObject доступний лише з iOS 14 і вище. Для iOS 13 використовуйте @ObservedObject і створюйте ObservableObject у батьківському View через @State з ручним керуванням життєвим циклом. Альтернатива — використовувати @State з struct замість class для даних, які не вимагають reference semantics.
Дочірній View створить власну копію ObservableObject, повністю незалежну від батьківської. Зміни в одному не відобразяться на іншому. Це майже завжди помилка: використовуйте @ObservedObject для отримання об'єкта від батька та @StateObject лише для створення нового об'єкта всередині View.
Об'єкт знищується, коли View, яке його створило, повністю видаляється з ієрархії SwiftUI. Для екрана в NavigationStack це відбувається при pop з навігаційного стека. Для модального вікна — при його закритті. Для TabView — при перемиканні вкладки, якщо View не кешується.
Використовуй кастомний init з доступом до property wrapper через підкреслення: _viewModel = StateObject(wrappedValue: MyViewModel(param: value)). Цей патерн дозволяє передати будь-які параметри в ObservableObject, зберігаючи при цьому гарантію єдиного створення об'єкта за час життя View.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також