@ObservedObject — это Property Wrapper в SwiftUI для наблюдения за экземпляром ObservableObject, переданным извне. В отличие от @StateObject, @ObservedObject не создаёт объект — он подписывается на изменения уже существующего. По данным Apple Developer Documentation (2025), @ObservedObject используется в дочерних view, которым нужно отслеживать данные, принадлежащие родителю. @ObservedObject обеспечивает реактивную связь без управления жизненным циклом объекта.
Главное
@ObservedObject — это Property Wrapper, который подписывает view на изменения ObservableObject. ObservableObject — это протокол из фреймворка Combine, который требует реализации objectWillChange publisher. Когда любое свойство, помеченное @Published, изменяется внутри ObservedObject, publisher отправляет сигнал, и SwiftUI перерисовывает все view, подписанные через @ObservedObject.
Главное свойство @ObservedObject — отсутствие владения. View не отвечает за создание или уничтожение объекта. Объект создаётся в родительском view (через @StateObject) или внедряется через @EnvironmentObject. Дочернее view только наблюдает за изменениями и получает обновления. Если объект будет заменён в родителе, @ObservedObject переключится на новый экземпляр.
@ObservedObject подходит для сценариев с разделением данных: модель пользователя, общие настройки, состояние подключения к серверу. Когда несколько view на разных уровнях иерархии должны отображать одни и те же данные, @ObservedObject в каждом view создаёт независимые, но согласованные подписки на один источник.
Разница между @ObservedObject и @StateObject — одна из самых частых тем вопросов на собеседованиях по SwiftUI. Основное правило: @StateObject создаёт и владеет объектом, @ObservedObject наблюдает за уже существующим. Нарушение этого правила приводит к неожиданной потере данных или двойной инициализации.
| Характеристика | @StateObject | @ObservedObject |
|---|---|---|
| Создание объекта | Да, при инициализации view | Нет, получает готовый |
| Владение | Текущее view | Родительский компонент |
| Единственный экземпляр | Да, на весь lifecycle | Нет, может быть заменён |
| Пересоздание при рендере | Нет, сохраняется | Зависит от родителя |
| Где использовать | Корневое view-владелец | Дочерние view |
@StateObject гарантирует, что объект создаётся один раз и переживает повторные инициализации структуры view. @ObservedObject получает объект извне и пересоздаётся при каждой инициализации родительской структуры. Если родитель использует @StateObject для объекта, дочерние view могут безопасно применять @ObservedObject — объект будет един на всю иерархию.
Механизм отслеживания @ObservedObject базируется на Combine и протоколе ObservableObject. При инициализации SwiftUI вызывает objectWillChange publisher — объект должен испускать сигнал перед изменением @Published-свойства. Combine передаёт сигнал в dependency graph SwiftUI, который помечает все зависимые view как требующие обновления. Это происходит синхронно перед изменением значения.
class WeatherService: ObservableObject {
@Published var temperature: Double = 22.0
@Published var city: String = "Moscow"
}
struct WeatherView: View {
@ObservedObject var weather: WeatherService
var body: some View {
VStack {
Text("\(weather.city)")
Text("\(weather.temperature)°C")
}
}
}
В листинге WeatherService — ObservableObject с двумя @Published-свойствами. WeatherView объявляет @ObservedObject var weather: WeatherService, получая экземпляр от родителя. Когда temperature изменяется, objectWillChange срабатывает до установки нового значения, SwiftUI перерисовывает WeatherView, и отображается актуальная температура. Подписка автоматически управляется SwiftUI — разработчику не нужно вызывать sink или dispose.
Первый паттерн — передача модели через инициализатор. Родитель создаёт ObservableObject через @StateObject и передаёт его дочерним view как @ObservedObject. Это стандартная иерархическая передача данных, при которой корневой view управляет жизненным циклом модели, а все вложенные компоненты подписываются на изменения.
Второй паттерн — EnvironmentObject, глобальная версия @ObservedObject через SwiftUI Environment. Объект внедряется на уровне сцены или корневого view и автоматически доступен всем дочерним компонентам без явной передачи через инициализаторы. Внутри дочернего view @EnvironmentObject работает аналогично @ObservedObject, но получает объект из окружения.
Третий паттерн — композиция нескольких ObservableObject. В сложных приложениях view может наблюдать за несколькими объектами: @ObservedObject var user: UserService, @ObservedObject var network: NetworkMonitor. Это разделяет ответственность между сервисами и сохраняет testability каждого компонента.
struct DashboardView: View {
@ObservedObject var user: UserViewModel
@ObservedObject var network: NetworkMonitor
var body: some View {
VStack {
Text("Welcome, \(user.name)")
HStack {
Circle()
.fill(network.isConnected ? Color.green : Color.red)
.frame(width: 10, height: 10)
}
}
}
}
DashboardView наблюдает за UserViewModel и NetworkMonitor. Каждый объект отвечает за свою область данных и независимо уведомляет view об изменениях. Если сеть отключается, NetworkMonitor меняет isConnected, и SwiftUI перерисовывает DashboardView, обновляя цвет индикатора. Композиция ObservableObject — предпочтительный способ организации данных в SwiftUI-приложениях.
@Published — это Property Wrapper из Combine, который автоматически добавляет издателя к свойству внутри ObservableObject. Когда @Published-свойство изменяется, Combine генерирует событие через objectWillChange publisher. SwiftUI подписывается на этот publisher при использовании @ObservedObject или @StateObject и перерисовывает view при каждом новом значении.
@Published поддерживает все типы, включая опциональные, коллекции и пользовательские структуры. Однако для коллекций (массивы, словари) SwiftUI отслеживает только замену ссылки, а не изменение содержимого. Чтобы обнаружить добавление или удаление элемента, нужно переприсвоить коллекцию целиком либо использовать ObservableObject с manual objectWillChange.send().
Важная деталь: @Published должен находиться только внутри класса, реализующего ObservableObject. Использование @Published вне ObservableObject вызовет ошибку компиляции. Также @Published нельзя применять к свойствам ленивой инициализации (lazy var) или вычисляемым свойствам (computed property).
Самая критичная ошибка — использование @ObservedObject для создания объекта. Если написать @ObservedObject var model = UserViewModel() в родительском view, при каждом рендере будет создаваться новый экземпляр UserViewModel. Данные будут потеряны, а @Published-подписки пересозданы. Всегда используйте @StateObject для создания и @ObservedObject только для получения готового объекта.
Вторая ошибка — изменение @Published-свойств вне основного потока. ObservableObject использует Combine, который требует отправки изменений на главном потоке (main actor). Если изменить @Published в фоновой очереди, SwiftUI может перерисовать view в неподходящий момент, вызывая race conditions. Используйте DispatchQueue.main.async или @MainActor для обновления.
Третья проблема — циклические обновления. Если изменение @Published включает побочные эффекты, которые снова изменяют @Published, возникает бесконечный цикл перерисовок. Решение: использовать флаги-защитники (isUpdating) или разносить логику на разные ObservableObject с чёткими границами ответственности.
Часто задаваемые вопросы
Да, SwiftUI поддерживает @ObservedObject var model: UserViewModel?. Однако view не будет подписан на изменения, пока объект nil. При присвоении значения подписка активируется автоматически.
@ObservedObject получает объект через инициализатор, @EnvironmentObject — через SwiftUI Environment. @EnvironmentObject не требует явной передачи через конструкторы, но объект должен быть внедрён на верхнем уровне иерархии.
Вызовите objectWillChange.send() до изменения свойства. Это полезно, если @Published не подходит (например, для вычисляемых свойств или операций с коллекциями, где нужно сообщить об изменении до мутации).
@ObservedObject и @Published отслеживают замену ссылки, а не мутацию содержимого коллекции. Для перерисовки нужно переприсвоить массив: items.append(newItem) → items = items или использовать objectWillChange.send() перед мутацией.
Нет, @ObservedObject — это SwiftUI Property Wrapper, доступный только внутри типов, реализующих протокол View. Для обычных структур используйте Combine напрямую с ObservableObjectPublisher.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также