swiftswiftuienvironmentobject

When using SwiftUI EnvironmentObjects, I get the error saying missing ObservableObject


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

struct ContentView: View {
    @StateObject var spotifyData = SpotifyDataManager()
    @StateObject var spotify = SpotifyAuthManager()
    @AppStorage("signedIn") var isSignedIn: Bool = false
    @State private var selection: Int = 0
    @StateObject var viewModel = ContentViewModel()
    @StateObject var regViewModel = RegistrationViewModel()
    
    var body: some View {
        NavigationStack {
            Group {
                if viewModel.userSession == nil {
                    SignInOptionsView()
                        .environmentObject(regViewModel)
                } else {
                    VStack {
                        //If the user is signed, the tab view is shown
                        if isSignedIn {
                            TabView(selection: $selection) {
                                HomeView()
                                    .tabItem {
                                        if selection == 0 {
                                            Image(systemName: "house.fill")
                                        } else {
                                            Image(systemName: "house")
                                                .environment(\.symbolVariants, .none)
                                        }
                                    }.tag(0)
                                
                                SearchView()
                                    .tabItem {
                                        Image(systemName: "magnifyingglass")
                                    }.tag(1)
                                
    //                            CreateView()
    //                                .tabItem {
    //                                    if selection == 2 {
    //                                        Image(systemName: "plus.app.fill")
    //                                    } else {
    //                                        Image(systemName: "plus.app")
    //                                            .environment(\.symbolVariants, .none)
    //                                    }
    //                                }.tag(2)
                                
    //                            TrendingView()
    //                                .tabItem {
    //                                    Image(systemName: "chart.line.uptrend.xyaxis")
    //                                }.tag(3)
                                
                                ProfileView()
                                    .tabItem {
                                        if selection == 4 {
                                            Image(systemName: "person.circle.fill")
                                        } else {
                                            Image(systemName: "person.circle")
                                                .environment(\.symbolVariants, .none)
                                        }
                                    }.tag(2)
                            }
                        } else {
                            //shows the signin view if the user is not signed in
                            SignInView()
                        }
                    }
                    .accentColor(.primary)
                    .onOpenURL { url in
                        Task {
                            await spotify.HandleURLCode(url)
                        }
                    }
                    //this requests a new token when needed
                    .task {
                        if SpotifyAuthManager.shouldRefresh {
                            try? await SpotifyAuthManager.getRefreshedAccessToken()
                        }
                    }
                    .toolbar {
                        ToolbarItem(placement: .navigationBarLeading) {
                            Text("JamaGram")
                                .foregroundColor(.primary)
                                .font(.system(size: 25))
                        }

                        ToolbarItem(placement: .navigationBarTrailing) {
                            NavigationLink {
                                Text("Notifications")
                            } label: {
                                Image(systemName: "bell")
                                    .foregroundColor(.primary)
                                    .font(.system(size: 20))
                            }
                        }

                        ToolbarItem(placement: .navigationBarTrailing) {
                            NavigationLink {
                                CreateView()
                            } label: {
                                Image(systemName: "plus.app")
                                    .foregroundColor(.primary)
                                    .font(.system(size: 20))
                            }
                        }

            //                ToolbarItem(placement: .navigationBarTrailing) {
            //                    NavigationLink {
            //                        Text("Messages")
            //                    } label: {
            //                        Image(systemName: "message")
            //                            .foregroundColor(.primary)
            //                            .font(.system(size: 20))
            //                    }
            //                }
                    }
                }
            }
        }
    }
}

The parent view

struct SignInOptionsView: View {
    var body: some View {
        NavigationStack {
            VStack {
                Spacer()
                
                Text("JamaGram")
                    .foregroundColor(.primary)
                    .font(.largeTitle)
                    .padding(.vertical)
                
                Text("Welcome to JamaGram! The best place to share your favorite music with friends.")
                    .multilineTextAlignment(.center)
                    .foregroundColor(.secondary)
                    .font(.headline)
                
                VStack(spacing: 30) {
                    NavigationLink {
                        SignInView()
                            .navigationBarBackButtonHidden()
                    } label: {
                        LargeButtonView(title: "Sign in with Email")
                    }
                    
                    NavigationLink {
                        SignInView()
                    } label: {
                        LargeButtonView(title: "Sign in with Apple")
                    }
                    
                    NavigationLink {
                        SignInView()
                    } label: {
                        LargeButtonView(title: "Sign in with Google")
                    }
                }
                .padding(.vertical)
                
                Spacer()
                
                NavigationLink {
                    AddEmailView()
                        .navigationBarBackButtonHidden()
                } label: {
                    Text("Don't have an account? Sign up now!")
                        .foregroundColor(Color("MainColor"))
                        .padding(.vertical)
                        .font(.footnote)
                }
            }
            .padding(.vertical)
            .padding(.horizontal, 18)
        }
    }
}

The child view where the error occurs

struct AddEmailView: View {
    @Environment(\.dismiss) var dismiss
    @EnvironmentObject var viewModel: RegistrationViewModel
    
    var body: some View {
        VStack(spacing: 25) {
            Spacer()
            
            Text("Enter your email address")
                .foregroundColor(.primary)
                .font(.title)
                .multilineTextAlignment(.center)
            
            Text("This email address will be used to log in to your account. It will not be visible to others.")
                .multilineTextAlignment(.center)
                .foregroundColor(.secondary)
                .font(.headline)
                .padding(.horizontal, 18)
            
            TextField("Email address", text: $viewModel.email)
                .modifier(TextFieldModifier())
                .autocapitalization(.none)
                .keyboardType(.emailAddress)
            
            NavigationLink {
                AddPasswordView()
                    .navigationBarBackButtonHidden()
            } label: {
                LargeButtonView(title: "Next")
            }
            
            Spacer()
            
            Text("1/4")
                .foregroundColor(.secondary)
                .font(.footnote)
                .fontWeight(.semibold)
                .padding(.vertical)
        }
        .toolbar {
            ToolbarItem(placement: .navigationBarLeading) {
                Button {
                    dismiss()
                } label: {
                    Image(systemName: "chevron.left")
                        .foregroundColor(.primary)
                        .font(.system(size: 20))
                }
            }
        }
    }
}

I have seen similar issues where people have been using sheets with environment objects but that is not the case in my code so I'm not sure what's causing the issue.

I tried to inject the environmentObject on the SignInOptionsView which I hoped would share the object with the addEmailView but at pressing the NavigationLink that says don't have an account? I get the error Fatal error: No ObservableObject of type RegistrationViewModel found. A View.environmentObject(_:) for RegistrationViewModel may be missing as an ancestor of this view.


Solution

    1. When using SwiftUI.TabView the tab view must always be at the top, each tab can have its own NavigationStack but a tab view cannot be inside a of one.

    Tab bars use bar items to navigate between mutually exclusive panes of content in the same view.

    https://developer.apple.com/design/human-interface-guidelines/tab-bars

    TabView > NavigationStack-tabItem 
    

    NOT

    NavigationStack > TabView > NavigationStack-tabItem
    
    1. The environmentObject() must be injected onto the parent, in this case the NavigationStack if the other tabs don't need it or the TabView if multiple tabs need it.
    TabView {
    
    }.environmentObject(regViewModel)
    

    or

    TabView {
    
        NavigationStack {
    
        }
        .environmentObject(regViewModel)
        .tabItem {
    
        }
    
    }
    

    Here is another link that can help.

    EnvironementObject not injected for navigationDestination