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

17,283

Solution 1

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

Solution 2

if you are using sheet ore navigation you have to pass the environment Object maybe its a bug of swiftUI but it works for me in Xcode 11.5:

.sheet(isPresented: $show){
        mySheetView().environmentObject(self.myEnviromentObject)
}

Solution 3

This problem seems to be cause by using two instances of NetworkManager in your code.

If you add this line to the NetworkManager:

final class NetworkManager: … {

    static let shared = NetworkManager()

}

And then update the references in your two views like this:

struct EarthQuakeList: … {

    @ObservedObject var networkManager = NetworkManager.shared

    …

}

struct EditPage: … {

    @ObservedObject var networkManager = NetworkManager.shared

    …

}

This will make sure both views use the exact same object to access the information. By using the same object, updates in one view will cause the other view to be updated as well.

Once this works, I recommend to play around with .environmentObject() (example) in SwiftUI as this would allow for sharing the same instance of these service types across your application.

Good luck with your project!

Share:
17,283
Sky
Author by

Sky

Updated on June 08, 2022

Comments

  • Sky
    Sky about 2 years

    my first view can get all the data from API request, then opened second view to change the API request data, it crashed.

    below is the error

    Fatal error: No ObservableObject of type NetworkManager found.
    A View.environmentObject(_:) for NetworkManager may be missing as an ancestor of this view.: file /BuildRoot/Library/Caches/com.apple.xbs/Sources/Monoceros_Sim/Monoceros-39.3/Core/EnvironmentObject.swift, line 55
    2019-11-07 12:00:01.961425-0800 EarthQuake[73703:5913116] Fatal error: No ObservableObject of type NetworkManager found.
    A View.environmentObject(_:) for NetworkManager may be missing as an ancestor of this view.: file /BuildRoot/Library/Caches/com.apple.xbs/Sources/Monoceros_Sim/Monoceros-39.3/Core/EnvironmentObject.swift, line 55
    

    This is swift file to fetch data from API, then save data to posts

    import Foundation
    import Combine
    
    final class NetworkManager: ObservableObject {
    
        @Published var posts = [Post]()
    
    
        func fetchData() {
    
             //some code to fetch data, then save to posts
    
        }
    
    }
    

    In my first list view file: EarthQuakeList.swift

    
    import SwiftUI
    
    struct EarthQuakeList: View {
    
        @EnvironmentObject var networkManager: NetworkManager
    
        //@ObservedObject var networkManager = NetworkManager()
    
        var body: some View {
    
            NavigationView {
                List(networkManager.posts) { post in
                    NavigationLink(destination: EarthQuakeDetail(detail: post.properties, location: post.locationCoordinate)) {
                        ListRow(magnitude: post.properties.mag ?? 0.0, place: post.properties.place)
    
                    }
    
                }
              .navigationBarTitle(Text("Today"))      
                .navigationBarItems(
    
                        trailing:
                        VStack {
    
                            Button(action: {
                                print("Edit button tapped")
                                self.showEditPage.toggle()
                            }) {
                                //Top right icon
                                Image(systemName: "pencil.circle")
                                    .resizable()
                                    .frame(width: 20, height: 20, alignment: .center)
                            }.sheet(isPresented: $showEditPage) { 
                                return EditPage().environmentObject(self.networkManager)
    
                            }
    
                        }
    
    
    
    
                )
    
    
    
            }
            .onAppear {
                //Call function to fetch data
                self.networkManager.fetchData()
            }
        }
    }
    

    Everything works well, I can see all the data, but when I do the same thing in my second view file, I get the data back, but it does not update for the first view

    In my second editing view file: EditPage.swift

    import SwiftUI
    
    struct EditPage: View {
    
        //@ObservedObject var networkManager = NetworkManager()
    
        @EnvironmentObject var networkManager: NetworkManager
    
        var body: some View {
    
            VStack {
                Text("Edite")
    
    
            }
            .onAppear(){
    
                //I called the same function to get data as in first view file, but it is not updating the list view 
                self.networkManager.fetchData()
    
    
            }
    
        }
    }
    

    SceneDelegate.swift

    import UIKit
    import SwiftUI
    
    class SceneDelegate: UIResponder, UIWindowSceneDelegate {
    
        var window: UIWindow?
    
    
        func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    
            //Set up environment object
            let contentView = EarthQuakeList().environmentObject(NetworkManager())
    
            // Use a UIHostingController as window root view controller.
            if let windowScene = scene as? UIWindowScene {
                let window = UIWindow(windowScene: windowScene)
                window.rootViewController = UIHostingController(
                    rootView: contentView
                )
                self.window = window
                window.makeKeyAndVisible()
            }
        }
    

    So I am expecting second view file to call function self.networkManager.fetchData() to get data from API call, then update list view in first swift file EarthQuakeList, the issue is I am just getting correct data back, the view is not updating with new data