SwiftUI Environment Object Not Found: Causes & Fix
App crashes with 'No ObservableObject of type found' when accessing an EnvironmentObject that isn't provided in the view hierarchy.
The Problem
When trying to access an EnvironmentObject in a SwiftUI view without properly injecting it into the view hierarchy, your app will crash at runtime with an error like:
Fatal error: No ObservableObject of type SomeType found. A View.environmentObject(_:) for SomeType must exist in the hierarchy.
Here's an example of the incorrect code that causes this issue:
// ❌ Wrong
class MyModel: ObservableObject {
@Published var value = "Hello"
}
struct ContentView: View {
@EnvironmentObject var model: MyModel
var body: some View {
Text(model.value)
}
}
// Usage in App struct or Scene
var body: some Scene {
WindowGroup {
ContentView()
}
}
Root Cause
The @EnvironmentObject property wrapper allows a view to access an object provided higher up in the view hierarchy using .environmentObject(_:). SwiftUI relies on this mechanism to propagate shared data down the view tree. If no such object is provided before accessing it in a view, SwiftUI will crash to prevent undefined behavior. This is a design decision to enforce correct dependency management and avoid silent bugs when objects are missing.
The Fix
To resolve the issue, you must inject the EnvironmentObject at the appropriate level in the view hierarchy — typically just above the view that needs it. Here's how to properly provide the object:
// ✅ Fixed
class MyModel: ObservableObject {
@Published var value = "Hello"
}
struct ContentView: View {
@EnvironmentObject var model: MyModel
var body: some View {
Text(model.value)
}
}
// Correct usage in App struct or Scene
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(MyModel())
}
}
By calling .environmentObject(MyModel()) on the root view (or a parent view), the instance becomes available to all descendant views that request it via @EnvironmentObject.
Alternative Approaches
- Use a shared instance (Singleton): You can define your model as a singleton and access it directly instead of using
EnvironmentObject. This is useful for global state but makes testing and modularity harder.
class MyModel: ObservableObject {
@Published var value = "Hello"
static let shared = MyModel()
}
// In view
@StateObject private var model = MyModel.shared
- Pass as Binding or Init Parameter: For more localized sharing, you can pass the object explicitly to child views via their initializers or bindings. This keeps dependencies explicit and avoids reliance on the environment.
Affected iOS Versions
This behavior is consistent across iOS versions where SwiftUI is available (iOS 13 and later). It is not a bug, but rather the intended behavior of @EnvironmentObject. Apple has not changed this mechanism as of iOS 17.