@Environment в SwiftUI — property wrapper для читання значень з системного середовища, які автоматично поширюються по ієрархії View. Компонент надає доступ до кольорової схеми, локалі, розміру шрифту, managedObjectContext та десятків інших системних параметрів. Згідно з Документацією Apple Developer (2025), @Environment гарантує, що будь-яка зміна значення середовища викличе перемалювання всіх підписаних View, забезпечуючи реактивне оновлення інтерфейсу без ручних викликів.
Головне
@Environment — це property wrapper SwiftUI, призначений для читання значень з системного середовища. Середовище — це ієрархічний контейнер значень, який SwiftUI автоматично поширює від батьківських View до дочірніх. Кожне значення середовища ідентифікується ключем — типом, що відповідає протоколу EnvironmentKey.
Механізм середовища нагадує dependency injection на рівні фреймворку: система надає попередньо визначений набір значень — кольорова схема (світла/темна), локаль, розмір шрифту, 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. Для кастомних ключів розробник сам визначає значення за замовчуванням в методі 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) — контекст, що передається через середовище з контейнера персистенції. Для навігації доступні 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 без props.
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. Кастомні EnvironmentKeys можуть мати setter в розширенні, але зміна через нього не тригерить оновлення UI — це технічно можливо, але не рекомендується.
@Binding створює двосторонній зв’язок з джерелом істини (State, StateObject, ObservableObject). @Environment — це одностороннє читання з ієрархічного контексту. @Binding підходить для передачі даних дочірній View, @Environment — для доступу до системних або глобальних налаштувань.
Визначте структуру, що реалізує протокол EnvironmentKey зі статичним 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 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також