@ObservedObject: что это, наблюдение за объектами и обновление View

Автор: IT Sectr Опубликовано: 2026-06-26 Время чтения: 9 мин

@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 для наблюдения за ObservableObject, созданным в родительском View.
  • Не владеет объектом — в отличие от @StateObject, @ObservedObject не управляет жизненным циклом объекта.
  • Подписка на изменения — при изменении @Published свойств View автоматически перерисовывается.
  • Передача через инициализатор — объект передаётся в дочернее View через параметр инициализатора.
  • iOS 13+ — @ObservedObject доступен с первой версии SwiftUI, в отличие от @StateObject (iOS 14+).

Что такое @ObservedObject в SwiftUI

@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

Механизм @ObservedObject основан на протоколе ObservableObject из Combine framework. Каждый класс, соответствующий ObservableObject, автоматически получает publisher objectWillChange, который отправляет сигнал перед изменением любого @Published свойства. SwiftUI подписывается на этот publisher через @ObservedObject и при получении сигнала помечает View как требующую перерисовки.

swift
class TaskViewModel: ObservableObject {
    @Published var tasks: [Task] = []
    @Published var isLoading = false
    
    func loadTasks() async {
        isLoading = true
        // fetch data
        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 vs @StateObject: когда что использовать

Разница между @ObservedObject и @StateObject — это разница между наблюдателем и владельцем. @StateObject создаёт объект и управляет его жизненным циклом. @ObservedObject только наблюдает за объектом, который был создан и хранится в другом месте. Выбор между ними определяется ответственностью View за данные.

СценарийРекомендацияПричина
View создаёт данные@StateObjectView владеет объектом и отвечает за его жизненный цикл
View получает данные@ObservedObjectView только наблюдает, объект живёт в родителе
Поддержка iOS 13@ObservedObject@StateObject недоступен, используйте @ObservedObject с ручным управлением
Переиспользуемый компонент@ObservedObjectКомпонент не должен создавать данные — он получает их извне

Главное правило: если View создаёт объект — @StateObject. Если View принимает объект — @ObservedObject. Нарушение этого правила в сторону @ObservedObject для создания объекта приводит к потере данных при перестроении View. Нарушение в сторону @StateObject для принятия объекта создаёт дублирующийся экземпляр, независимый от родительского.

Примеры использования @ObservedObject

Типичный сценарий использования @ObservedObject — список задач, где корневой View создаёт view model, а ячейка списка получает её через @ObservedObject. Каждая ячейка может вызывать методы view model, а изменения автоматически отображаются во всему списке, поскольку все ячейки наблюдают за одним и тем же объектом.

swift
struct TaskRow: View {
    @ObservedObject var viewModel: TaskViewModel
    let task: Task
    
    var body: some View {
        HStack {
            Text(task.title)
            Spacer()
            Button("Done") {
                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

Самая распространённая ошибка — использование @ObservedObject для создания объекта внутри View. Когда View перестраивается (например, при изменении state), SwiftUI создаёт новый экземпляр ObservableObject, что приводит к потере всех накопленных данных. Эта ошибка особенно болезненна в NavigationStack, где пользователь может заполнить форму и потерять данные при возврате назад.

Ошибка: @ObservedObject вместо @StateObject

swift
// ❌ Data loss: @ObservedObject does not retain object
struct FormView: View {
    @ObservedObject var formVM = FormViewModel()
    // New formVM created on each View rebuild!
}

// ✅ Correct: @StateObject retains the object
struct FormView: View {
    @StateObject var formVM = FormViewModel()
    // Object created once per View lifetime
}

Ошибка: передача @StateObject там, где нужен @ObservedObject

Если дочернее View объявляет тот же ObservableObject через @StateObject, оно создаёт независимую копию. Изменения в родительском объекте не будут видны в дочернем, и наоборот. Всегда используй @ObservedObject для дочерних View, которые получают объект извне.

Альтернативы @ObservedObject в SwiftUI

В современной SwiftUI существует несколько альтернатив @ObservedObject, каждая со своими преимуществами. Выбор зависит от архитектуры приложения, версии iOS и конкретного сценария использования.

  • @EnvironmentObject — позволяет получить объект из окружения SwiftUI без явной передачи через инициализатор. Удобен для объектов, которые нужны многим экранам, но требует явного внедрения через .environmentObject().
  • @State + @Binding — для простых value-типов не нужны ObservableObject. Используй @State для хранения и @Binding для передачи в дочерние View.
  • @AppStorage — для UserDefaults-значений, которые должны автоматически синхронизироваться с View.
  • @SceneStorage — для сохранения временного состояния между перезапусками сцены (например, позиция в списке).

Выбор между @ObservedObject и @EnvironmentObject — это вопрос стиля и архитектуры. @ObservedObject явно показывает зависимости View через инициализатор, что делает код более предсказуемым. @EnvironmentObject удобен для глубоких иерархий, но скрывает зависимости, что может затруднить отладку.

Часто задаваемые вопросы

Можно ли использовать @ObservedObject без @StateObject в родителе?

Да, если объект создаётся и хранится вне SwiftUI — например, в AppDelegate или синглтоне. В этом случае @ObservedObject просто подписывается на изменения существующего объекта. Однако для объектов, создаваемых внутри SwiftUI-иерархии, всегда нужен @StateObject где-то выше.

Почему @ObservedObject иногда не обновляет View?

Наиболее вероятная причина — свойство изменяется не через @Published или изменяется не сам объект, а его внутренняя структура без вызова objectWillChange. Для коллекций используй присваивание новой копии: array.append() недостаточно — нужно присвоить сам массив заново через array = array + [element].

Влияет ли @ObservedObject на производительность?

@ObservedObject сам по себе не создаёт значительной нагрузки. Проблемы возникают при частых изменениях @Published свойств — каждое изменение триггерит перерисовку всех наблюдающих View. Для оптимизации используй EquatableView, уменьшай количество published-свойств и избегай лишних обновлений.

Чем @ObservedObject отличается от @Binding?

@ObservedObject наблюдает за целым классом ObservableObject и перерисовывает View при любом изменении его published-свойств. @Binding создаёт двустороннюю связь с конкретным значением (String, Int, Bool) и позволяет читать и записывать его. @Binding легче и не требует ObservableObject.

Можно ли комбинировать @ObservedObject с @Published в одном классе?

Да, это стандартный паттерн. @Published внутри ObservableObject автоматически интегрируется с @ObservedObject. Каждое @Published свойство добавляет наблюдателя к objectWillChange publisher. При изменении любого из них все View с @ObservedObject для этого объекта перерисовываются.

Итоги

  • @ObservedObject — property wrapper для наблюдения за ObservableObject, созданным в другом месте иерархии.
  • Не владеет объектом — в отличие от @StateObject, @ObservedObject не управляет жизненным циклом и не создаёт объект.
  • Подписка через Combine — SwiftUI автоматически подписывается на objectWillChange publisher ObservableObject.
  • iOS 13+ — @ObservedObject доступен с первой версии SwiftUI, что важно для проектов со старой поддержкой.
  • Передача через инициализатор — объект передаётся в дочернее View явно, что делает зависимости прозрачными.
  • Ошибка владения — использование @ObservedObject для создания объекта приводит к потере данных при перестроении View.
  • Альтернативы — @EnvironmentObject для внедрения через окружение, @State/@Binding для value-типов.

Мы разработаем мобильное приложение под ключ

IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.

Обсудить проект

Читайте также