В чем разница между модификатором .environment() и .environmentObject() в SwiftUI?

«В чем разница между модификатором .environment() и .environmentObject() в SwiftUI?» — вопрос из категории SwiftUI, который задают на 10% собеседований IOS Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Оба метода передают данные вниз по иерархии представлений SwiftUI, но предназначены для разных типов данных и имеют разные правила использования.

@Environment и .environment(_:value:)

Используется для передачи значений, соответствующих заранее определенным ключам (EnvironmentKey). Часто это системные настройки или собственные контекстные значения.

Пример: Чтение системного значения и установка своего:

// 1. Определение своего ключа окружения
struct ThemeKey: EnvironmentKey {
    static let defaultValue = Color.blue
}

extension EnvironmentValues {
    var customTheme: Color {
        get { self[ThemeKey.self] }
        set { self[ThemeKey.self] = newValue }
    }
}

// 2. Установка значения с помощью .environment
ContentView()
    .environment(.customTheme, .green) // Устанавливаем свое значение
    .environment(.colorScheme, .dark)  // Устанавливаем системное значение

// 3. Чтение значения в дочернем виде
struct ChildView: View {
    @Environment(.customTheme) var themeColor
    @Environment(.colorScheme) var colorScheme // Системное

    var body: some View {
        Text("Hello")
            .foregroundColor(themeColor)
    }
}

@EnvironmentObject и .environmentObject(_:)

Используется для передачи экземпляра класса, соответствующего протоколу ObservableObject. Предназначен для разделяемых моделей данных, которые могут изменяться и вызывать обновление UI.

Пример:

// 1. ObservableObject класс модели
class UserSettings: ObservableObject {
    @Published var username = "Alex"
}

// 2. Создание и внедрение объекта в корне иерархии
@main
struct MyApp: App {
    @StateObject var settings = UserSettings() // Владение

    var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(settings) // Передача вниз
        }
    }
}

// 3. Получение и использование в любом дочернем виде
struct ProfileView: View {
    @EnvironmentObject var settings: UserSettings // Не требует инициализации

    var body: some View {
        TextField("Name", text: $settings.username)
    }
}
Сводка отличий: Признак @Environment / .environment() @EnvironmentObject / .environmentObject()
Тип данных Любое значение (Value-type) Только ссылочный тип, реализующий ObservableObject
Ключ Требуется предопределенный EnvironmentKey Используется тип самого объекта в качестве ключа
Использование Системные настройки (.colorScheme), темы, флаги Разделяемые модели данных (UserSettings, AppState)
Безопасность Имеет defaultValue, всегда возвращает значение Вызовет краш, если объект не был передан до использования
Обновление UI Обновляет вид, только если значение изменилось (Hashable) Автоматически обновляет вид при изменении @Published свойств