@Environment w SwiftUI — property wrapper do odczytu wartości ze środowiska systemowego, automatycznie rozpowszechnianych w hierarchii View. Komponent zapewnia dostęp do schematu kolorów, locale, rozmiaru czcionki, managedObjectContext i dziesiątek innych parametrów systemowych. Według Apple Developer Documentation (2025), @Environment gwarantuje, że każda zmiana wartości środowiska powoduje przerysowanie wszystkich subskrybowanych View, zapewniając reaktywne aktualizowanie interfejsu bez ręcznych wywołań.
Najważniejsze
@Environment — to property wrapper SwiftUI przeznaczony do odczytu wartości ze środowiska systemowego. Środowisko to hierarchiczny kontener wartości, który SwiftUI automatycznie rozpowszechnia od rodzicielskich View do potomnych. Każda wartość środowiska jest identyfikowana przez klucz — typ zgodny z protokołem EnvironmentKey.
Mechanizm środowiska przypomina dependency injection na poziomie frameworka: system udostępnia predefiniowany zestaw wartości — schemat kolorów (light/dark), locale, rozmiar czcionki, managedObjectContext dla Core Data, dismiss do zamykania ekranu i wiele innych. View, która zadeklarowała @Environment z określonym kluczem, automatycznie otrzymuje bieżącą wartość i przerysowuje się przy jej zmianie.
Architektura środowiska SwiftUI opiera się na protokole EnvironmentValues — strukturze zawierającej wszystkie wartości systemowe. Każda wartość jest przechowywana jako właściwość tej struktury z getterem i setterem. @Environment używa key path do dostępu do konkretnej właściwości: @Environment(\.colorScheme) — dostęp do schematu kolorów, @Environment(\.locale) — do locale.
Property wrapper @Environment implementuje dwa kluczowe mechanizmy: odczyt wartości ze środowiska i subskrypcję jego zmian. Podczas tworzenia View SwiftUI przechodzi przez wszystkie @Environment-właściwości i wiąże je z odpowiednimi wartościami z bieżącego kontekstu. Jeśli rodzicielska View zmieni wartość przez modyfikator .environment(), wszystkie potomne View czytające tę wartość automatycznie się przerysowują.
Ważna cecha: @Environment obsługuje wartości opcjonalne. Jeśli wartość nie jest ustawiona w hierarchii, zwracana jest wartość domyślna zdefiniowana w EnvironmentKey. Dla kluczy systemowych wartość domyślna jest zawsze rozsądna — na przykład schemat kolorów domyślnie .light. Dla niestandardowych kluczy programista sam określa wartość domyślną w metodzie defaultValue protokołu EnvironmentKey.
struct EnvironmentReaderView: View {
@Environment(\.colorScheme) var colorScheme
@Environment(\.locale) var locale
@Environment(\.sizeCategory) var sizeCategory
var body: some View {
VStack {
Text("Aktualny schemat: \(colorScheme == .dark ? "Dark" : "Light")")
Text("Locale: \(locale.identifier)")
Text("Rozmiar czcionki: \(sizeCategory)")
}
}
}
W przykładzie View odczytuje trzy systemowe wartości środowiska. Przy zmianie colorScheme — na przykład użytkownik włączył tryb ciemny w ustawieniach — View automatycznie przerysowuje się z nową wartością. Podobnie przy zmianie regionu lub rozmiaru czcionki (Dynamic Type). View nie musi subskrybować powiadomień ani wywoływać odświeżenia — SwiftUI zarządza tym automatycznie.
SwiftUI udostępnia dziesiątki systemowych wartości środowiska, obejmujących różne aspekty interfejsu i zachowania. Schemat kolorów (\.colorScheme) — jedna z najczęściej używanych wartości, pozwalająca dostosować interfejs do jasnego i ciemnego motywu. Locale (\.locale) zawiera regionalne ustawienia użytkownika do formatowania dat, liczb i walut.
Dla Core Data używany jest managedObjectContext (\.managedObjectContext) — kontekst przekazywany przez środowisko z persistence container. Do nawigacji dostępne są dismiss (\.dismiss) do zamykania bieżącego ekranu i isPresented (\.isPresented) do widoków modalnych. Dla kalendarza i strefy czasowej — odpowiednio calendar i timeZone.
| Key Path | Typ | Przeznaczenie |
|---|---|---|
| \.colorScheme | ColorScheme | Jasny lub ciemny motyw |
| \.locale | Locale | Ustawienia regionalne |
| \.sizeCategory | ContentSizeCategory | Rozmiar czcionki Dynamic Type |
| \.managedObjectContext | NSManagedObjectContext | Kontekst Core Data |
| \.dismiss | DismissAction | Zamykanie ekranu |
| \.calendar | Calendar | Bieżący kalendarz |
| \.timeZone | TimeZone | Strefa czasowa |
| \.horizontalSizeClass | UserInterfaceSizeClass | Poziomy rozmiar ekranu |
Aby uzyskać dostęp do wartości systemowych, użyj key path przez kropkę: @Environment(\.dismiss) var dismiss. Kompilator sprawdza istnienie key path w EnvironmentValues, dlatego błędny klucz spowoduje błąd na etapie kompilacji. Nowe wartości systemowe są dodawane przez Apple z każdą wersją iOS — aktualna lista dostępna jest w dokumentacji EnvironmentValues.
Mimo podobnych nazw, @Environment i @EnvironmentObject rozwiązują różne zadania. @Environment odczytuje systemowe lub niestandardowe wartości zarejestrowane przez EnvironmentKey. @EnvironmentObject — to property wrapper dla ObservableObject, przekazywanego przez środowisko według typu, bez jawnego klucza.
@EnvironmentObject jest używany do dependency injection: rodzicielska View tworzy obiekt (na przykład ViewModel) i przekazuje go potomnym View przez modyfikator .environmentObject(). Potomne View otrzymują go przez @EnvironmentObject i mogą zarówno odczytywać, jak i zmieniać jego właściwości. @Environment natomiast — tylko do odczytu wartości systemowych i nie obsługuje sprzężenia zwrotnego.
| Parametr | @Environment | @EnvironmentObject |
|---|---|---|
| Przeznaczenie | Wartości systemowe i niestandardowe | Iniekcja ObservableObject |
| Klucz | Key path EnvironmentValues | Według typu obiektu |
| Zapis | Tylko odczyt | Odczyt i zapis |
| Wartość niestandardowa | Przez EnvironmentKey | Przez klasę ObservableObject |
| Wartość domyślna | Jest (defaultValue) | Brak (należy przekazać) |
W praktyce: używaj @Environment do dostępu do parametrów systemowych (motyw, locale, rozmiar czcionki) i niestandardowych konfiguracji, które nie zmieniają się w runtime. Używaj @EnvironmentObject do przekazywania ViewModel lub serwisu przez hierarchię View, gdy wymagana jest zmiana stanu z potomnych komponentów.
Rozważmy tworzenie niestandardowej wartości środowiska. W tym celu należy zdefiniować strukturę zgodną z protokołem EnvironmentKey i rozszerzyć EnvironmentValues o nową właściwość. Pozwala to przekazywać konfigurację motywu lub ustawienia aplikacji przez całe drzewo View bez propsów.
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 }
Protokół EnvironmentKey wymaga implementacji statycznej właściwości defaultValue — wartości, która zostanie użyta, jeśli rodzicielska View nie ustawiła niestandardowego środowiska. Rozszerzenie EnvironmentValues dodaje obliczaną właściwość appTheme, używając subscript z kluczem. Następnie każda View może odczytać wartość przez @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("Tryb ciemny aktywny")
.foregroundStyle(.white)
.background(Color.black)
} else {
Text("Tryb jasny aktywny")
.foregroundStyle(.black)
.background(Color.white)
}
}
}
}
struct ContentView: View {
@State private var selectedTheme = AppTheme.system
var body: some View {
ThemedView()
.environment(\.appTheme, selectedTheme)
}
}
ThemedView odczytuje dwa środowiska: niestandardowe appTheme i systemowe colorScheme. Kombinacja pozwala zaimplementować elastyczne ustawienie motywu: użytkownik może wybrać motyw „Jasny”, „Ciemny” lub „Systemowy”. Jeśli wybrany jest systemowy — wartość pobierana jest z colorScheme, które automatycznie zmienia się przy przełączaniu motywu w ustawieniach iOS. Rodzicielska View (ContentView) ustawia wartość appTheme przez modyfikator .environment().
struct ModalView: View {
@Environment(\.dismiss) var dismiss
@State private var name = ""
var body: some View {
NavigationStack {
Form {
TextField("Your name", text: $name)
Button("Zapisz") { dismiss() }
}
.navigationTitle("Edit Profile")
}
}
}
Ten przykład demonstruje praktyczne użycie dismiss — instancji DismissAction ze środowiska. Wywołanie dismiss() jako funkcji zamyka modalny ekran lub cofa NavigationLink. Jedynym wymaganiem jest, aby View była prezentowana modalnie lub znajdowała się wewnątrz NavigationStack. dismiss automatycznie określa się z kontekstu: jeśli View jest otwarta jako sheet — zamyka sheet, jeśli jako popover — zamyka popover.
Często zadawane pytania
Nie, @Environment jest przeznaczony tylko do odczytu. Do zmiany wartości używaj @EnvironmentObject z ObservableObject lub @Binding. Niestandardowe EnvironmentKey mogą mieć setter w rozszerzeniu, ale zmiana przez niego nie wyzwala aktualizacji UI — jest to technicznie możliwe, ale niezalecane.
@Binding tworzy dwukierunkowe połączenie ze źródłem prawdy (State, StateObject, ObservableObject). @Environment — jednokierunkowy odczyt z kontekstu hierarchicznego. @Binding nadaje się do przekazywania danych do potomnej View, @Environment — do dostępu do ustawień systemowych lub globalnych.
Zdefiniuj strukturę implementującą protokół EnvironmentKey ze static defaultValue. Następnie rozszerz EnvironmentValues o właściwość z getterem/setterem przez subscript[key]. Po rejestracji używaj @Environment(\.yourKey) do odczytu i .environment(\.yourKey, value) do ustawienia.
SwiftUI udostępnia ponad 50 systemowych wartości: colorScheme, locale, sizeCategory, managedObjectContext, dismiss, calendar, timeZone, horizontalSizeClass, verticalSizeClass, accessibilityEnabled, layoutDirection, legibilityWeight i inne. Pełna lista w dokumentacji EnvironmentValues.
Tak, @Environment działa w Preview, ale wartości domyślne mogą różnić się od symulatora. Do testowania w Preview używaj modyfikatora .environment() bezpośrednio w kodzie Preview: ThemedView().environment(\.colorScheme, .dark). Pozwala to wizualnie sprawdzać różne stany środowiska.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również