Разница между @StateObject, @EnvironmentObject и @ObservedObject в SwiftUI

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Перевод статьи подготовлен в преддверии старта курса "iOS Developer. Professional".


Эту неделю я решил посвятить потокам данных в SwiftUI. В этой статье мы обсудим разницу между обертками свойств (property wrappers) @StateObject, @EnvironmentObject, и @ObservedObject, поскольку я знаю, что это самая запутанная тема для новичков в SwiftUI.

Зачем нужны обертки свойств в SwiftUI?

SwiftUI использует неизменяемые (immutable) типы структур для описания иерархии представлений. Все представления, которые предоставляет SwiftUI, неизменяемые. Вот почему SwiftUI дает нам набор оберток свойств - для обработки изменений данных. Обертки свойств позволяют нам объявлять себя внутри представлений SwiftUI, но хранить данные вне представления, объявляющего обертку.

@StateObject

@StateObject - это новая обертка свойства, которая инициализирует экземпляр класса,  соответствующего протоколу ObservableObject, и сохраняет его во внутренней памяти фреймворка SwiftUI. SwiftUI создает только один @StateObject для каждого контейнера, который его объявляет, и хранит его вне жизненного цикла представления. Давайте посмотрим на несколько примеров, в которых мы используем @StateObject для сохранения состояния целого приложения.

import SwiftUI
 
@main
struct CardioBotApp: App {
    @StateObject var store = Store(
        initialState: AppState(),
        reducer: appReducer,
        environment: AppEnvironment(service: HealthService())
    )
 
    var body: some Scene {
        WindowGroup {
            RootView().environmentObject(store)
        }
    }
}

 

Как видите, @StateObject идеально подходит для хранения состояния целого приложения и передачи его различным сценам или представлениям внутри вашего приложения. SwiftUI сохранит его в специальной памяти фреймворка, чтобы ваши данные находились в безопасном месте вне сцены или жизненного цикла представления.

 Если хотите узнать больше о реализации концепции Single State Container, читайте мою статью «Redux-подобный контейнер состояний в SwiftUI. Основы».

@ObservedObject

@ObservedObject это еще один способ подписаться и следить за изменениями в ObservableObject. SwiftUI не контролирует жизненный цикл @ObservedObject - вы должны следить за этим самостоятельно. @ObservedObject идеально подходит для случая, когда у вас есть ObservableObject, хранящийся в @StateObject, и вам нужно поделиться им с каким-либо реюзабельным представлением.

 

Я упомянул реюзабельные представления потому что я сам использую CalendarContainerView в нескольких местах в моем приложении, и я не хочу, чтобы оно зависело от внешних условий. Я использую @ObservedObject, чтобы явно указать данные, используемые представлением в данном конкретном случае.

NavigationLink(
    destination: CalendarContainerView(
        store: transformedStore,
        interval: .twelveMonthsAgo
    )
) {
    Text("Calendar")
}

Если хотите узнать больше об использовании Container View, читайте мою статью «Redux-подобный контейнер состояний в SwiftUI. Container Views".

@EnvironmentObject

@EnvironmentObject - отличный способ неявно внедрить экземпляр класса, который соответствует ObservableObject, в часть иерархии представления. Предположим, в вашем приложении есть модуль, который содержит 3-4 экрана, и все они используют одну и ту же ViewModel. Если вы не хотите явно передавать одну и ту же ViewModel из одного представления в другое, тогда вам понадобится @EnvironmentObject. Давайте посмотрим, как мы можем его использовать.

@main
struct CardioBotApp: App {
    @StateObject var store = Store(
        initialState: AppState(),
        reducer: appReducer,
        environment: .production
    )
 
    var body: some Scene {
        WindowGroup {
            TabView {
                NavigationView {
                    SummaryContainerView()
                        .navigationBarTitle("today")
                        .environmentObject(
                            store.derived(
                                deriveState: \.summary,
                                embedAction: AppAction.summary
                            )
                        )
                }
 
                NavigationView {
                    TrendsContainerView()
                        .navigationBarTitle("trends")
                        .environmentObject(
                            store.derived(
                                deriveState: \.trends,
                                embedAction: AppAction.trends
                            )
                        )
                }
            }
        }
    }
}

 

В приведенном выше примере мы внедряем environmentObject в иерархию представлений SummaryContainerView. SwiftUI неявно предоставит доступ для внедренных environmentObject-ов ко всем дочерним представлениям, которые находятся внутри SummaryContainerView. Мы можем быстро получить и подписаться на внедренные environmentObject-ы, используя обертку свойства @EnvironmentObject.

struct SummaryContainerView: View {
    @EnvironmentObject var store: Store<SummaryState, SummaryAction>
 
    var body: some View {
        //......

 

Я должен упомянуть, что @EnvironmentObject имеет тот же жизненный цикл, что и @ObservedObject. Это означает, что вы можете получить новый environmentObject всякий раз, когда создаете его внутри представления, которое может быть воссоздано с помощью SwiftUI.

Если хотите узнать больше о продвинутых методах использования Single State Container, читайте мою статью «Redux-подобный контейнер состояний в SwiftUI. Лучшие практики".

Заключение

Сегодня мы поговорили о различиях между обертками свойств @StateObject, @EnvironmentObject, и @ObservedObject. Надеюсь, эта статья поможет вам понять, какая обертка свойства лучше всего подходит для вашего случая. Не стесняйтесь подписываться на меня в Твиттере и задавать свои вопросы, связанные с этой статьей. Спасибо за ваше внимание, до встречи на следующей неделе!

 

Узнать подробнее о курсе.

Источник: https://habr.com/ru/company/otus/blog/527364/


Интересные статьи

Интересные статьи

SwiftUI – это молодая и пока что не совсем изученная технология. С одной стороны, большое пространство для творчества и исследования, а с другой – неизвестность, нестабил...
Недавно на проекте интегрировал модуль CRM Битрикса c виртуальной АТС Ростелеком. Делал по стандартной инструкции, где пошагово показано, какие поля заполнять. Оказалось, следование ей не гаран...
Новый сервис WebWormHole работает как портал, через который файлы передаются с компьютера на другой. Нажимаете кнопку New Wormhole — и получаете код для входа. Человек с другой стороны вводит...
Несмотря на то, что “в коробке” с Битриксом уже идут модули как для SOAP (модуль “Веб сервисы” в редакции “Бизнес” и старше), так и для REST (модуль “Rest API” во всех редакциях, начиная с...
От скорости сайта зависит многое: количество отказов, брошенных корзин. Согласно исследованию Google, большинство посетителей не ждёт загрузки больше 3 секунд и уходит к конкурентам. Бывает, что сайт ...