Fix '@EnvironmentObject may be missing as an ancestor' — and how is @Environment different?
Tapping "Settings" crashes with "@EnvironmentObject may be missing as an ancestor of this view". The root injects session, but the presented sheet still can't find it.
Requirements:
- Explain why the sheet's view can't see the injected object.
- Fix it so
SettingsViewreceivessession.
struct RootView: View {
@StateObject private var session = Session()
@State private var showSettings = false
var body: some View {
Button("Settings") { showSettings = true }
.environmentObject(session)
.sheet(isPresented: $showSettings) {
SettingsView() // crashes: no session in the sheet's environment
}
}
}
struct SettingsView: View {
@EnvironmentObject var session: Session
var body: some View { Text(session.username) }
}
Find and fix the bug.
A view read an @EnvironmentObject no ancestor injected via .environmentObject(_:). Inject it on the sheet or navigation destination that needs it — those don't inherit the presenter's environment. @Environment differs — a keyed value with a default.
- ✗Not injecting the object into a sheet or navigation destination's environment
- ✗Confusing
@EnvironmentObject(injected object) with@Environment(keyed value) - ✗Making the property optional to silence the crash instead of injecting upstream
- →Why doesn't a presented sheet inherit the presenter's environment object?
- →Why can
@Environmentnever crash for a missing value the way@EnvironmentObjectdoes?
A presented sheet (and a navigationDestination, and a fullScreenCover) starts a new environment branch — it does not automatically inherit the .environmentObject(_:) values of the view that presented it. So session is injected on the button, but SettingsView inside the sheet is in a different branch and reads an object nobody supplied, which traps at runtime.
Inject the object on the sheet's content directly:
.sheet(isPresented: $showSettings) {
SettingsView()
.environmentObject(session) // supply it into the sheet's branch
}
@Environment is different in kind: it reads a keyed value from the environment (\.dismiss, \.colorScheme, \.locale) and every key has a default value, so a missing injection just yields the default — it can never crash the way @EnvironmentObject does. Use @EnvironmentObject for your own model objects (and always inject them), and @Environment for system-provided keyed values.