@ObservedObject — је property wrapper у SwiftUI који омогућава View-у да посматра промене у ObservableObject-у креираном на другом месту хијерархије. За разлику од @StateObject, @ObservedObject не креира објекат — он само претплаћује на његов publisher objectWillChange и прецртава View при ажурирању @Published својстава. Ово чини @ObservedObject правим избором за подређена View која добијају податке од родитеља кроз иницијализатор. Према чланку Paul Hudson — Hacking with Swift (2025), типична архитектура SwiftUI апликације се гради овако: корени View користи @StateObject за креирање view model-а, а сва подређена View га добијају кроз @ObservedObject, што обезбеђује јединствени извор истине без дуплирања података.
Главно
@ObservedObject — је property wrapper који претплаћује View на промене ObservableObject-а. Када објекат означен са @ObservedObject промени било које од својих својстава декларисаних са @Published, SwiftUI аутоматски прецртава View. @ObservedObject не креира објекат — он само успоставља везу између већ постојеће инстанце ObservableObject-а и View-а које треба да реагује на његове промене.
Кључна разлика између @ObservedObject и @StateObject — је власништво. @ObservedObject претпоставља да је објекат креиран и ускладиштен негде горе у хијерархији View-а. Подређено View добија референцу на овај објекат кроз иницијализатор и једноставно га посматра. Ако подређени View буде поново креиран, добиће исту референцу од родитеља — подаци се не губе.
Према Apple Developer Documentation — SwiftUI (2025), @ObservedObject је доступан од iOS 13, што га чини једином опцијом за посматрање ObservableObject-а у пројектима који подржавају старе верзије iOS-а. У iOS 14+ пожељније је користити @StateObject за креирање објеката, али @ObservedObject остаје актуелан за пренос постојећих објеката.
Механизам @ObservedObject се заснива на протоколу ObservableObject из Combine framework-а. Свака класа која је у складу са ObservableObject аутоматски добија publisher objectWillChange који шаље сигнал пре промене било ког @Published својства. SwiftUI се претплаћује на овај publisher кроз @ObservedObject и по пријему сигнала означава View као да захтева прецртавање.
class TaskViewModel: ObservableObject {
@Published var tasks: [Task] = []
@Published var isLoading = false
func loadTasks() async {
isLoading = true
// преузми податке
isLoading = false
}
}
struct TaskListView: View {
@ObservedObject var viewModel: TaskViewModel
var body: some View {
List(viewModel.tasks) { task in
Text(task.title)
}
.task { await viewModel.loadTasks() }
}
}
Када родитељски View преноси viewModel у TaskListView кроз иницијализатор, SwiftUI креира везу између објекта и View-а. При промени низа tasks или флага isLoading, SwiftUI прецртава TaskListView. Објекат притом остаје непромењен — чува се у родитељском View-у кроз @StateObject.
Разлика између @ObservedObject и @StateObject — је разлика између посматрача и власника. @StateObject креира објекат и управља његовим животним циклусом. @ObservedObject само посматра објекат који је креиран и ускладиштен на другом месту. Избор између њих је одређен одговорношћу View-а за податке.
| Сценарио | Препорука | Разлог |
|---|---|---|
| View креира податке | @StateObject | View поседује објекат и одговоран је за његов животни циклус |
| View добија податке | @ObservedObject | View само посматра, објекат живи у родитељу |
| Подршка за iOS 13 | @ObservedObject | @StateObject недоступан, користите @ObservedObject са ручним управљањем |
| Компонента за вишекратну употребу | @ObservedObject | Компонента не треба да креира податке — добија их из споља |
Главно правило: ако View креира објекат — @StateObject. Ако View прима објекат — @ObservedObject. Кршење овог правила у смеру @ObservedObject за креирање објекта доводи до губитка података при поновној изградњи View-а. Кршење у смеру @StateObject за пријем објекта креира дуплирану инстанцу, независну од родитељске.
Типичан сценариј коришћења @ObservedObject — листа задатака, где корени View креира view model, а ћелија листе га добија кроз @ObservedObject. Свака ћелија може позивати методе view model-а, а промене се аутоматски приказују на целој листи, јер све ћелије посматрају исти објекат.
struct TaskRow: View {
@ObservedObject var viewModel: TaskViewModel
let task: Task
var body: some View {
HStack {
Text(task.title)
Spacer()
Button("Готово") {
viewModel.completeTask(task)
}
}
}
}
struct TaskListContainer: View {
@StateObject var viewModel = TaskViewModel()
var body: some View {
List(viewModel.tasks) { task in
TaskRow(viewModel: viewModel, task: task)
}
}
}
У овом примеру TaskListContainer креира viewModel кроз @StateObject, а свака TaskRow га добија кроз @ObservedObject. Када корисник кликне „Done” у било ком реду, viewModel.completeTask мења published својство, и сви View-ови који посматрају овај објекат се аутоматски ажурирају.
Најчешћа грешка — коришћење @ObservedObject за креирање објекта унутар View-а. Када се View поново изгради (нпр. при промени state-а), SwiftUI креира нову инстанцу ObservableObject-а, што доводи до губитка свих прикупљених података. Ова грешка је посебно болна у NavigationStack-у, где корисник може попунити формулар и изгубити податке при повратку назад.
// ❌ Губитак података: @ObservedObject не задржава објекат
struct FormView: View {
@ObservedObject var formVM = FormViewModel()
// Нови formVM се креира при свакој поновној изградњи View-а!
}
// ✅ Исправно: @StateObject задржава објекат
struct FormView: View {
@StateObject var formVM = FormViewModel()
// Објекат креиран једном током живота View-а
}
Ако подређено View декларише исти ObservableObject кроз @StateObject, креира независну копију. Промене у родитељском објекту неће бити видљиве у подређеном и обрнуто. Увек користите @ObservedObject за подређена View која добијају објекат из споља.
У савременом SwiftUI постоји неколико алтернатива за @ObservedObject, свака са својим предностима. Избор зависи од архитектуре апликације, верзије iOS-а и конкретног сценарија коришћења.
Избор између @ObservedObject и @EnvironmentObject — је питање стила и архитектуре. @ObservedObject експлицитно показује зависности View-а кроз иницијализатор, што чини код предвидљивијим. @EnvironmentObject је погодан за дубоке хијерархије, али скрива зависности, што може отежати отклањање грешака.
Често постављана питања
Да, ако се објекат креира и чува ван SwiftUI — на пример, у AppDelegate или синглтону. У овом случају @ObservedObject се једноставно претплаћује на промене постојећег објекта. Међутим, за објекте креиране унутар SwiftUI хијерархије увек је потребан @StateObject негде изнад.
Највероватнији разлог — својство се мења не кроз @Published или се не мења сам објекат, већ његова унутрашња структура без позивања objectWillChange. За колекције користите доделу нове копије: array.append() није довољно — потребно је поново доделити цео низ кроз array = array + [element].
@ObservedObject сам по себи не ствара значајно оптерећење. Проблеми настају при честим променама @Published својстава — свака промена покреће прецртавање свих посматрачких View-ова. За оптимизацију користите EquatableView, смањите број published својстава и избегавајте непотребна ажурирања.
@ObservedObject посматра целу класу ObservableObject и прецртава View при свакој промени његових published својстава. @Binding креира двосмерну везу са одређеном вредношћу (String, Int, Bool) и омогућава читање и писање исте. @Binding је лакши и не захтева ObservableObject.
Да, то је стандардни образац. @Published унутар ObservableObject-а се аутоматски интегрише са @ObservedObject. Свако @Published својство додаје посматрача publisher-у objectWillChange. При промени било ког од њих, сви View-ови са @ObservedObject за овај објекат се прецртавају.
Резиме
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође