@EnvironmentObject — je property wrapper ve SwiftUI, který umožňuje libovolnému View v hierarchii přístup k ObservableObject bez explicitního předávání přes řetězec inicializátorů. Objekt je vložen do prostředí pomocí modifikátoru .environmentObject() na určité úrovni hierarchie, poté všechna podřízená View jej mohou získat přes @EnvironmentObject. Tím odpadá nutnost předávat objekt přes zprostředkující View, která jej nepoužívají — tzv. prop drilling. Podle článku John Sundell — Swift by Sundell (2025) je @EnvironmentObject zvláště užitečný pro data mezi obrazovkami: uživatelská relace, nastavení aplikace, správce nákupního košíku nebo lokální cache dat.
Hlavní body
@EnvironmentObject — je property wrapper, který umožňuje SwiftUI View přístup k ObservableObject z prostředí (environment) aplikace. Prostředí je kontejner, do kterého můžete umístit objekty na libovolné úrovni hierarchie View pomocí modifikátoru .environmentObject(). Po umístění objektu do prostředí jej může libovolné podřízené View získat jednoduše deklarováním vlastnosti s @EnvironmentObject a určením typu objektu.
Hlavním úkolem @EnvironmentObject — řešení problému předávání dat přes hlubokou hierarchii View bez nutnosti předávat objekt přes každou zprostředkující úroveň. Ve složitých aplikacích s rozvětvenou strukturou NavigationStack, TabView a modálních oken @EnvironmentObject výrazně zjednodušuje architekturu tím, že odstraňuje boilerplate kód.
Podle Apple Developer Documentation — Environment (2025) používá @EnvironmentObject vnitřní mechanismus SwiftUI založený na PreferenceKey a identifikaci View. Každé View ukládá odkaz na své vlastní prostředí, které je zděděno z nadřazeného View a může být rozšířeno pomocí .environmentObject(). Vyhledávání objektu probíhá směrem nahoru v hierarchii až ke kořenovému View.
class UserSession: ObservableObject {
@Published var isLoggedIn = false
@Published var userName: String = ""
func login(name: String) {
userName = name
isLoggedIn = true
}
}
@main
struct MyApp: App {
@StateObject var session = UserSession()
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(session)
}
}
}
@EnvironmentObject funguje na základě mechanismu vkládání závislostí (DI) zabudovaného do SwiftUI. Když zavoláte .environmentObject() na View, SwiftUI uloží objekt do speciálního úložiště přidruženého k tomuto View a všem jeho potomkům. Když podřízené View deklaruje @EnvironmentObject stejného typu, SwiftUI hledá objekt v prostředí, postupuje nahoru v hierarchii rodičů.
Důležitá vlastnost — typ objektu se používá jako klíč pro vyhledávání v prostředí. Pokud jsou v prostředí dva objekty stejného typu, SwiftUI najde ten nejbližší aktuálnímu View v hierarchii. Při vkládání objektu na úrovni WindowGroup se stává globálně dostupným pro všechny obrazovky aplikace, což je vhodné pro služby obecného účelu.
Podle objc.io — SwiftUI Architecture (2025) interně @EnvironmentObject používá mechanismus podobný @ObservedObject, ale s dodatečnou úrovní abstrakce pro vyhledávání objektu v hierarchii. SwiftUI objekt nekopíruje ani nevytváří — předává odkaz na existující instanci, takže změny v objektu jsou automaticky viditelné pro všechna View používající @EnvironmentObject.
Jak @EnvironmentObject, tak @ObservedObject plní stejnou základní funkci — přihlašují View ke změnám ObservableObject. Rozdíl je v mechanismu předávání objektu. @ObservedObject vyžaduje explicitní předání přes inicializátor, zatímco @EnvironmentObject získává objekt z prostředí bez explicitního uvádění v každém zprostředkujícím View.
| Vlastnost | @EnvironmentObject | @ObservedObject |
|---|---|---|
| Předávání | Přes .environmentObject() na úrovni hierarchie | Přes inicializátor každého View |
| Explicitnost závislostí | Skryté — neviditelné v signatuře View | Explicitní — viditelné v init View |
| Zprostředkující View | Neví o objektu | Musí předat objekt dále |
| Riziko chyby | Runtime crash při absenci objektu | Kontrola při kompilaci (pokud je parametr povinný) |
| Prop drilling | Odstraňuje | Vyžaduje ruční předávání |
Výběr mezi @EnvironmentObject a @ObservedObject závisí na architektuře. Pokud je objekt potřebný hluboko v hierarchii a pro mnoho obrazovek — @EnvironmentObject je pohodlnější. Pokud architektura vyžaduje explicitní uvádění závislostí pro testování a čitelnost — @ObservedObject je vhodnější.
Nejčastější scénář — uživatelská relace, která by měla být dostupná na všech obrazovkách aplikace. Vložením UserSession přes .environmentObject() v kořeni aplikace může každá obrazovka přistupovat k datům uživatele a stavu autorizace.
struct ProfileView: View {
@EnvironmentObject var session: UserSession
var body: some View {
VStack {
if session.isLoggedIn {
Text("Ahoj, \(session.userName)")
Button("Odhlásit") {
session.isLoggedIn = false
}
} else {
LoginView()
}
}
}
}
struct SettingsView: View {
@EnvironmentObject var session: UserSession
var body: some View {
Form {
Text("Přihlášen jako \(session.userName)")
}
}
}
Všimněte si: ani ProfileView ani SettingsView nepřijímají session přes inicializátor. Jednoduše deklarují @EnvironmentObject var session: UserSession a SwiftUI automaticky najde objekt v prostředí. To umožňuje přidávat nové obrazovky bez změny stávajícího kódu pro předávání dat.
Hlavním rizikem @EnvironmentObject — runtime crash, pokud objekt nebyl vložen do prostředí. Na rozdíl od volitelných parametrů @EnvironmentObject nemůže být nil. Pokud se View s @EnvironmentObject objeví na obrazovce a nadřazené View nezavolalo .environmentObject() pro tento typ, aplikace okamžitě spadne s „Fatal error: No ObservableObject of type X found".
Pokud vložíte dva objekty stejného typu na různých úrovních hierarchie, podřízené View obdrží nejbližší v hierarchii. To může vést k zmatení, pokud vývojář očekává, že objekt z kořenového prostředí bude dostupný v modálním okně, které má vlastní prostředí s objektem stejného typu.
S rozvojem SwiftUI se objevily alternativní způsoby správy závislostí, které řeší některé nedostatky @EnvironmentObject — především neexplicitnost závislostí a riziko runtime crash.
Výběr přístupu závisí na velikosti týmu a složitosti aplikace. Pro malé projekty @EnvironmentObject funguje skvěle. Pro velké projekty s desítkami obrazovek a přísnými požadavky na testování je preferováno explicitní předávání přes @ObservedObject nebo DI kontejner.
Často kladené otázky
Ano, View může deklarovat libovolný počet @EnvironmentObject různých typů. SwiftUI hledá každý typ nezávisle v prostředí. To je výhodné, když View potřebuje přístup k uživatelské relaci, nastavením a nákupnímu košíku současně — každý objekt je vkládán samostatně.
Preview spadne s runtime error při pokusu o zobrazení View. Vždy přidávejte .environmentObject() v Preview pro View, která používají @EnvironmentObject. Používejte mock objekty s testovacími daty, aby Preview fungovalo správně a zobrazovalo realistický stav.
Ne, @EnvironmentObject funguje pouze s konkrétním typem třídy implementující ObservableObject. Pro protokoly musíte použít type erasure nebo obal: vytvořte třídu obalu, která ukládá odkaz na objekt protokolového typu, a vkládejte obal přes @EnvironmentObject.
Vytvořte instanci ObservableObject s testovacími daty a předejte ji View pomocí .environmentObject(testObject) v testu. Toto je standardní vzor pro testování UI ve SwiftUI. Pro jednotkové testy izolujte logiku v ObservableObject a testujte jej odděleně od View.
@EnvironmentObject nevytváří dodatečnou zátěž na výkon, protože pouze předává odkaz na objekt, nekopíruje jej. Avšak častá aktualizace @Published vlastností v globálním objektu může způsobit současné překreslení mnoha View, což může ovlivnit výkon.
Shrnutí
Vyvineme mobilní aplikaci na klíč
IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.
Přečtěte si také