UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

SOLVED: EnvironmentObject usage in init() of a View

Forums > SwiftUI

Why am I unable to use an EnvironmentObject var in the init of a View? When I try my app says "No ObservableObject of type ??? found".

However, I am able to use the var anywhere else in the View, e.g. in a button's tap event.

This does not work:

@EnvironmentObject var dal: DAL

@State private var myStuff: [Stuff] = []

init() {

  myStuff = dal.getMyStuff() // This fails at runtime: No ObservableObject of type DAL found.
}

But this does work:

@EnvironmentObject var dal: DAL

@State private var myStuff: [Stuff] = []

loadList() {

  myStuff = dal.getMyStuff()
}

If I try to call loadList from init, it will give the error. But if I call loadList from a tap event it will work correctly.

This seems like the View doesn't have access to the EnvironmentObject at initialisation time. Am I doing something wrong?

3      

The environment is passed down when the body is called, so it doesn't yet exist during the initialization phase of the View struct.

3      

Thank you.

If I use

.onAppear { loadList() }

then it works!

3      

I just need to thank you for making this topic: I've been tearing my hair out for hours trying to understand why (seemingly) NONE of my child Views were inheriting or able to access an object I placed in the environment.

I tried debugging by testing access to the environment within each View's init(), because that seems safe, right? Not at all. Finally your topic helped me understand what's happening.

I have no idea why this behavior exists. Is there any place in Apple's documentation that explains why (or even that at all it occurs) Views cannot access environment objects during their init() phase?

3      

I have no idea why this behavior exists.

Because when init is called on a View, that's no guarantee it will actually be displayed on screen. (e.g., the destination of a NavigationLink gets its init called when the NavigationLink is rendered, even if the user never follows the link) It's not until the body is rendered that it will be displayed and so that's when an environment needs to exist. If you used an environment object or value at init time, it could be different by the time the View is actually rendered.

6      

@roosterboy, thank you for explaning! I wish this were made more clear somewhere within Apple's own documentation.

Despite understanding why this happens, I now find myself needing to come up with hack-y (and, in my mind, distinctly non-Swift-y) workarounds for my use-case. I'm using Firestore to retrieve a specific document and display its details in a View, but I cannot pass an object directly into that View (because it will not be updated dynamically using Firestore's snapshots). So I need to pass the ID of that specific document to that View and then tell Firestore to retrieve that document based on its ID; the init() method feels like the smartest location to place that code, but given Swift's behavior as you just described, it cannot go there.

3      

BUILD THE ULTIMATE PORTFOLIO APP Most Swift tutorials help you solve one specific problem, but in my Ultimate Portfolio App series I show you how to get all the best practices into a single app: architecture, testing, performance, accessibility, localization, project organization, and so much more, all while building a SwiftUI app that works on iOS, macOS and watchOS.

Get it on Hacking with Swift+

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.