@Environment в SwiftUI — property wrapper для чтения значений из системного окружения, автоматически распространяемых по иерархии View. Компонент предоставляет доступ к цветовой схеме, локали, размеру шрифта, managedObjectContext и десяткам других системных параметров. По данным Apple Developer Documentation (2025), @Environment гарантирует, что любое изменение значения окружения вызывает перерисовку всех подписанных View, обеспечивая реактивное обновление интерфейса без ручных вызовов.
Главное
@Environment — это property wrapper SwiftUI, предназначенный для чтения значений из системного окружения. Окружение — это иерархический контейнер значений, который SwiftUI автоматически распространяет от родительских View к дочерним. Каждое значение окружения идентифицируется ключом — типом, соответствующим протоколу EnvironmentKey.
Механизм окружения напоминает dependency injection на уровне фреймворка: система предоставляет предопределённый набор значений — цветовая схема (light/dark), локаль, размер шрифта, managedObjectContext для Core Data, dismiss для закрытия экрана и многие другие. View, объявившая @Environment с определённым ключом, автоматически получает текущее значение и перерисовывается при его изменении.
Архитектура окружения SwiftUI основана на протоколе EnvironmentValues — структуре, содержащей все системные значения. Каждое значение хранится как свойство этой структуры с getter и setter. @Environment использует key path для доступа к конкретному свойству: @Environment(\.colorScheme) — доступ к цветовой схеме, @Environment(\.locale) — к локали.
Property wrapper @Environment реализует два ключевых механизма: чтение значения из среды и подписку на его изменения. При создании View SwiftUI проходится по всем @Environment-свойствам и связывает их с соответствующими значениями из текущего контекста. Если родительская View изменяет значение через модификатор .environment(), все дочерние View, читающие это значение, автоматически перерисовываются.
Важная особенность: @Environment поддерживает опциональные значения. Если значение не установлено в иерархии, возвращается значение по умолчанию, определённое в EnvironmentKey. Для системных ключей значение по умолчанию всегда разумно — например, цветовая схема по умолчанию .light. Для кастомных ключей разработчик сам определяет default-значение в методе defaultValue протокола EnvironmentKey.
struct EnvironmentReaderView: View {
@Environment(\.colorScheme) var colorScheme
@Environment(\.locale) var locale
@Environment(\.sizeCategory) var sizeCategory
var body: some View {
VStack {
Text("Current scheme: \(colorScheme == .dark ? "Dark" : "Light")")
Text("Locale: \(locale.identifier)")
Text("Font size: \(sizeCategory)")
}
}
}
В примере View читает три системных значения окружения. При изменении colorScheme — например, пользователь включил тёмную тему в настройках — View автоматически перерисовывается с новым значением. Аналогично при смене региона или размера шрифта (Dynamic Type). View не нужно подписываться на уведомления или вызывать refresh — SwiftUI управляет этим автоматически.
SwiftUI предоставляет десятки системных значений окружения, охватывающих различные аспекты интерфейса и поведения. Цветовая схема (\ .colorScheme) — одно из самых востребованных значений, позволяющее адаптировать интерфейс под светлую и тёмную тему. Локаль (\ .locale) содержит региональные настройки пользователя для форматирования дат, чисел и валют.
Для Core Data используется managedObjectContext (\ .managedObjectContext) — контекст, передаваемый через окружение из persistence container. Для навигации доступны dismiss (\ .dismiss) для закрытия текущего экрана и isPresented (\ .isPresented) для модальных представлений. Для календаря и часового пояса — calendar и timeZone соответственно.
| Key Path | Тип | Назначение |
|---|---|---|
| \.colorScheme | ColorScheme | Светлая или тёмная тема |
| \.locale | Locale | Региональные настройки |
| \.sizeCategory | ContentSizeCategory | Размер шрифта Dynamic Type |
| \.managedObjectContext | NSManagedObjectContext | Контекст Core Data |
| \.dismiss | DismissAction | Закрытие экрана |
| \.calendar | Calendar | Текущий календарь |
| \.timeZone | TimeZone | Часовой пояс |
| \.horizontalSizeClass | UserInterfaceSizeClass | Горизонтальный размер экрана |
Для доступа к системным значениям используйте key path через точку: @Environment(\.dismiss) var dismiss. Компилятор проверяет существование key path в EnvironmentValues, поэтому ошибочный ключ вызовет ошибку на этапе сборки. Новые системные значения добавляются Apple с каждой версией iOS — актуальный список доступен в документации EnvironmentValues.
Несмотря на похожие названия, @Environment и @EnvironmentObject решают разные задачи. @Environment читает системные или кастомные значения, зарегистрированные через EnvironmentKey. @EnvironmentObject — это property wrapper для ObservableObject, передаваемого через окружение по типу, без явного ключа.
@EnvironmentObject используется для dependency injection: родительская View создаёт объект (например, ViewModel) и передаёт его дочерним View через модификатор .environmentObject(). Дочерние View получают его через @EnvironmentObject и могут как читать, так и изменять его свойства. @Environment же — только для чтения системных значений и не поддерживает обратную связь.
| Параметр | @Environment | @EnvironmentObject |
|---|---|---|
| Назначение | Системные и кастомные значения | ObservableObject инъекция |
| Ключ | Key path EnvironmentValues | По типу объекта |
| Запись | Только чтение | Чтение и запись |
| Кастомное значение | Через EnvironmentKey | Через класс ObservableObject |
| Значение по умолчанию | Есть (defaultValue) | Нет (обязательно передать) |
На практике: используйте @Environment для доступа к системным параметрам (тема, локаль, размер шрифта) и кастомным конфигурациям, которые не меняются в рантайме. Используйте @EnvironmentObject для передачи ViewModel или сервиса через иерархию View, когда требуется изменять состояние из дочерних компонентов.
Рассмотрим создание кастомного значения окружения. Для этого нужно определить структуру, соответствующую протоколу EnvironmentKey, и расширить EnvironmentValues новым свойством. Это позволяет передавать конфигурацию темы или настройки приложения через всё дерево View без пропсы.
struct AppThemeKey: EnvironmentKey {
static let defaultValue: AppTheme = .system
}
extension EnvironmentValues {
var appTheme: AppTheme {
get { self[AppThemeKey.self] }
set { self[AppThemeKey.self] = newValue }
}
}
enum AppTheme { case system, light, dark }
Протокол EnvironmentKey требует реализации статического свойства defaultValue — значение, которое будет использоваться, если родительская View не установила кастомное окружение. Расширение EnvironmentValues добавляет вычисляемое свойство appTheme, используя subscript с ключом. После этого любая View может читать значение через @Environment(\.appTheme).
struct ThemedView: View {
@Environment(\.appTheme) var appTheme
@Environment(\.colorScheme) var colorScheme
var body: some View {
VStack {
if appTheme == .dark || (appTheme == .system && colorScheme == .dark) {
Text("Dark mode active")
.foregroundStyle(.white)
.background(Color.black)
} else {
Text("Light mode active")
.foregroundStyle(.black)
.background(Color.white)
}
}
}
}
struct ContentView: View {
@State private var selectedTheme = AppTheme.system
var body: some View {
ThemedView()
.environment(\.appTheme, selectedTheme)
}
}
ThemedView читает два окружения: кастомное appTheme и системное colorScheme. Комбинация позволяет реализовать гибкую настройку темы: пользователь может выбрать «Светлая», «Тёмная» или «Системная» тема. Если выбрана системная — значение берётся из colorScheme, которое автоматически меняется при переключении темы в настройках iOS. Родительская View (ContentView) устанавливает значение appTheme через модификатор .environment().
struct ModalView: View {
@Environment(\.dismiss) var dismiss
@State private var name = ""
var body: some View {
NavigationStack {
Form {
TextField("Your name", text: $name)
Button("Save") { dismiss() }
}
.navigationTitle("Edit Profile")
}
}
}
Этот пример демонстрирует практическое использование dismiss — экземпляра DismissAction из окружения. Вызов dismiss() как функции закрывает модальный экран или возвращает NavigationLink. Единственное требование — View должна быть представлена модально или находиться внутри NavigationStack. dismiss автоматически определяется из контекста: если View открыта как sheet — закрывается sheet, если как popover — закрывается popover.
Часто задаваемые вопросы
Нет, @Environment предназначен только для чтения. Для изменения значений используйте @EnvironmentObject с ObservableObject или @Binding. Кастомные EnvironmentKey могут иметь setter в расширении, но изменение через него не триггерит обновление UI — это технически возможно, но не рекомендуется.
@Binding создаёт двустороннюю связь с источником истины (State, StateObject, ObservableObject). @Environment — однонаправленное чтение из иерархического контекста. @Binding подходит для передачи данных в дочернюю View, @Environment — для доступа к системным или глобальным настройкам.
Определите структуру, реализующую протокол EnvironmentKey со static defaultValue. Затем расширьте EnvironmentValues свойством с getter/setter через subscript[key]. После регистрации используйте @Environment(\.yourKey) для чтения и .environment(\.yourKey, value) для установки.
SwiftUI предоставляет более 50 системных значений: colorScheme, locale, sizeCategory, managedObjectContext, dismiss, calendar, timeZone, horizontalSizeClass, verticalSizeClass, accessibilityEnabled, layoutDirection, legibilityWeight и другие. Полный список в документации EnvironmentValues.
Да, @Environment работает в Preview, но значения по умолчанию могут отличаться от симулятора. Для тестирования в Preview используйте модификатор .environment() прямо в Preview-коде: ThemedView().environment(\.colorScheme, .dark). Это позволяет визуально проверять различные состояния окружения.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также