Swiftui @Environmentobject Error: May Be Missing as an Ancestor of This View

SwiftUI @EnvironmentObject error: may be missing as an ancestor of this view

The whole issue is very easy to fix just add self.networkManager @ EarthQuakeList.swift

.sheet(isPresented: $showEditPage) { 
return EditPage().environmentObject(self.networkManager)

I got the reason and inspired from article:
https://github.com/peterfriese/Swift-EnvironmentObject-Demo

SwiftUI @EnvironmentObject error: may be missing as an ancestor of this view -- accessing object in the init()

The answer is that an Environment Object apparently cannot be accessed in an init() function. However, an ObservedObject can be. So I changed the code to this and it works. To make it easy I turned TState into a singleton that I could access anywhere. This could probably replace the use of @EnvironmentObject in many situations.

struct TEditorView: View {
@ObservedObject private var tState = TState.shared
//@EnvironmentObject private var tState: TState

@State var name = ""

init() {
self._name = State(initialValue: tState.name)
}

var body: some View {
...
}
}

A View.environmentObject(_:) may be missing as an ancestor of this view - but not always…

The reason your problem is intermittent, is probably because the PurchaseManager init()
could finish
before all the data is setup properly, due to the "delays" of the
async functions in init(). So sometimes the data will be available
when the View wants it, and sometimes it will not be there and crash your app.

You could try the following approach that includes @atultw advice of using
StateObject.

import SwiftUI

@main
struct TestApp: App {
@StateObject var purchaseManager = PurchaseManager() // <-- here

var body: some Scene {
WindowGroup {
MainView()
.onAppear {
purchaseManager.startMeUp() // <-- here
}
.environmentObject(purchaseManager)
}
}
}

struct MainView: View {
@EnvironmentObject var purchaseManager: PurchaseManager

var body: some View {
Text("testing")
List {
ForEach(purchaseManager.offerings, id: \.self) { offer in
Text(offer)
}
}
}
}

public class PurchaseManager: ObservableObject {
@Published var offerings: [String] = []

// -- here --
func startMeUp() {
// setupRevenueCat()
fetchOfferings()
// refreshPurchaserInfo()
}

func fetchOfferings() {
DispatchQueue.main.asyncAfter(deadline: .now()+2) {
self.offerings = ["offer 1","offer 2","offer 3","offer 4"]
}
}
}

A View.environmentObject(_:) for AppInformation may be missing as an ancestor of this view

So first of all, your example code is not working.

  • Use @Published for your properties, then use Slider(value: $appInfo.duration, ...). This is because Slider is expecting a Binding (see https://developer.apple.com/documentation/swiftui/slider)
  • Use the struct Settings_Previews outside of struct Settings

Below you will find the fixed version. Both running in previews and in Simulator.

To answer your question:

If you use Settings anywhere in your Code, you have to pass it an environmentObject of AppInformation (See ContentView). Since you did not provide any source code in context I assume thats where your error is.

Here is a working example:

import SwiftUI
import CoreData

class AppInformation: ObservableObject {
@Published var duration = 0.0
@Published var interval = 0.0
}

struct Settings: View {
@EnvironmentObject var appInfo: AppInformation

var body: some View {
NavigationView {
ZStack {
VStack (spacing: 35){
HStack {
Text("Duration")
.font(.system(size: 23, weight: .bold, design: .rounded))
.padding(.leading, 25)
Spacer()

Slider(value: $appInfo.duration, in: 1...60, step: 1)
.padding([.trailing, .leading], 20)
Text("\(Int(appInfo.duration))")
.padding(.trailing, 30)
.font(.system(size: 23, weight: .medium, design: .rounded))
}
.padding(.top, 100)

HStack {
Text("Interval ")
.font(.system(size: 23, weight: .bold, design: .rounded))
.padding(.leading, 25)
Spacer()

Slider(value: $appInfo.interval, in: 1...60, step: 1)
.padding([.trailing, .leading], 20)
Text("\(Int(appInfo.interval))")
.padding(.trailing, 30)
.font(.system(size: 23, weight: .medium, design: .rounded))

}
Spacer()
}
}
.navigationTitle("Settings")
}
}
}

struct Settings_Previews: PreviewProvider {
static var previews: some View {
Settings()
.environmentObject(AppInformation())
}
}

struct ContentView: View {
@ObservedObject var appInformation = AppInformation()

var body: some View {
Settings()
.environmentObject(appInformation)
}

}

Also here are some tips using SwiftUI:

  • If you need to set padding on multiple edges you can use also use .padding([.trailing, .leading], 20)
  • You should set the .navigationTitle("Settings") modifier on the outer child of the view - but within the NavigationView
  • You can also set the .font modifier on the VStack or HStack, this would apply it to all children (including the Slider Label)

No ObservableObject of type CarouselViewModel found. A View.environmentObject(_:) for CarouselViewModel may be missing as an ancestor of this view

Wherever you instantiate the Home view, you have to add an .environmentObject modifier that passes an instance of CarouselViewModel to it.

I don't know what your exact code looks like (or what CarouselViewModel takes as parameters), but it'll look something like:

//somewhere in the parent view:
@StateObject private var model = CarouselViewModel() //if iOS 13, use @ObservedObject

//where you instantiate `Home`:
Home().environmentObject(model)

Note that this will also be an error if you try to use Home in a SwiftUI Preview and it doesn't have this .environmentObject code.

SwiftUI Fatal error: No ObservableObject of type AppData found. A View.environmentObject(_:) for AppData may be missing as an ancestor of this view

EnvironmentObjects must be supplied by anscestor views!

  @main  
struct WFT_demo2App: App {
var body: some Scene {
WindowGroup {

ContentView(text: .constant(""))
.environmentObject(AppData()) // <<: Here!

}
}
}


Related Topics



Leave a reply



Submit