TEAM LICENSES: Save money and learn new skills through a Hacking with Swift+ team license >>

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      

Hacking with Swift is sponsored by RevenueCat.

SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure your entire paywall view without any code changes or app updates.

Learn more here

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.