How to pass EnvironmentObject into View Model in SwiftUI?
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
@EnvironmentObject is designed for SwiftUI views, not for plain view models. If a view model needs data that comes from the environment, the usual solution is to read the environment object in the view layer and inject the dependency into the view model explicitly.
Why direct access in the view model does not work
SwiftUI resolves @EnvironmentObject while it builds a view hierarchy. A view model is just a normal Swift type, so there is no automatic environment lookup happening inside it.
That is why this idea is the wrong mental model:
- the view declares
@EnvironmentObject - the view model somehow reads the same property wrapper on its own
Instead, treat the shared object like any other dependency and pass it in through an initializer or method.
Define the shared object normally
Suppose the app stores session state in a shared observable object:
SwiftUI views can read this through the environment. The view model should not know where it came from; it should only receive a SessionStore dependency.
Inject the dependency into the view model
Create the view model so it accepts the shared object explicitly.
This approach is testable and keeps the view model independent from SwiftUI environment mechanics.
Bridge environment to the view model with a wrapper view
The view reads the environment object, then passes it to a child view that owns the StateObject.
This wrapper pattern is common because the environment value is available in body, while StateObject construction needs explicit input during initialization.
Use narrower dependencies when possible
Often the view model does not need the full environment object. Passing a protocol or smaller service keeps coupling lower.
This becomes valuable when the environment object grows over time and only part of it belongs in the feature you are building.
Inject the environment at the app boundary
The environment object still has to be supplied by a parent view or the application root.
If this injection is missing, SwiftUI will crash when the view attempts to read the environment object. The view model is not the source of that failure; the missing environment configuration is.
Common Pitfalls
The most common mistake is declaring @EnvironmentObject directly inside the view model and expecting SwiftUI to fill it in. SwiftUI only manages that property wrapper for views in the hierarchy.
Another issue is trying to initialize a @StateObject from an environment object in the same view’s init. Environment values are not resolved there, so the code becomes awkward or fails outright. Use a wrapper view instead.
Developers also over-inject. If the view model only needs one property or service, do not pass a huge shared state object by default. A smaller dependency makes the model easier to test and reason about.
Finally, remember that architectural boundaries matter. The view layer should own SwiftUI concerns such as environment lookup, while the view model should own presentation logic built from explicit dependencies.
Summary
- '
@EnvironmentObjectbelongs in SwiftUI views, not plain view models.' - Read the environment object in a view and inject it into the view model explicitly.
- A wrapper view is the standard pattern when
StateObjectneeds environment-backed input. - Prefer narrow protocols or services when the view model does not need the full shared object.
- Supplying the environment at the app boundary is still required for the pattern to work.

