0

So, I'm getting the following error:

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

However, I do actually in fact have .environmentObject(AppViewModel()) on every preview and I'm not sure why.

Here's the code in question:

import SwiftUI

struct SignUpView: View {
    
    @State var email = ""
    @State var password = ""
    @EnvironmentObject var model:AppViewModel
    
    var body: some View {
        ZStack {
            Color.theme.blue
            
            RoundedRectangle(cornerRadius: 30, style: .continuous)
                .foregroundStyle(LinearGradient(colors: [.orange, .red], startPoint: .topLeading, endPoint: .bottomTrailing))
                .frame(width: 1000, height: 475)
                .rotationEffect(.degrees(15))
                .offset(x: 20)
            
            VStack(spacing: 20) {
                
                Image("logo")
                    .resizable()
                    .scaledToFill()
                    .frame(width: 100)
                    .offset(y: 10)
                
                Text("Welcome")
                    .foregroundColor(.white)
                    .font(Font.custom("Poppins-Bold", size: 44))
                
                Text("Please Sign Up")
                    .foregroundColor(Color.theme.blue)
                    .font(Font.custom("Poppins-Regular", size: 22))
                    .offset(y:-20)
                
                // Email TextField
                TextField("", text: $email)
                    .disableAutocorrection(true)
                    .autocapitalization(.none)
                    .foregroundColor(.white)
                    .textFieldStyle(.plain)
                    .placeholder(when: email.isEmpty) {
                        Text("Email Address")
                            .foregroundColor(.white)
                            .font(Font.custom("Poppins-Light", size: 20))
                    }
                
                // Email TextBox
                Rectangle()
                    .frame(width:350, height: 1)
                    .foregroundColor(.white)
                    .padding(.top, -5)
                
                // Password TextField
                SecureField("", text: $password)
                    .disableAutocorrection(true)
                    .autocapitalization(.none)
                    .foregroundColor(.white)
                    .textFieldStyle(.plain)
                    .placeholder(when: password.isEmpty) {
                        Text("Password")
                            .foregroundColor(.white)
                            .font(Font.custom("Poppins-Light", size: 20))
                    }
                
                // Password TextBox
                Rectangle()
                    .frame(width:350, height: 1)
                    .foregroundColor(.white)
                    .padding(.top, -5)
                
                Button {
                    guard !email.isEmpty, !password.isEmpty else {
                        return
                    }
                    
                    model.signUp(email: email, password: password)
                } label: {
                    Text("Sign Up")
                        .frame(width: 200, height: 40)
                        .background(
                            Color.theme.blue
//                            RoundedRectangle(cornerRadius: 10, style: .continuous)
//                                .fill(.linearGradient(colors: [.red, .orange], startPoint: .topTrailing, endPoint: .bottomTrailing))
                            )
                        .cornerRadius(10)
                        .foregroundColor(.white)
                        .font(Font.custom("Poppins-Medium", size: 18))
                }
                
                Spacer()
                // Login Link
                HStack {
                    Text("Already Have An Account?")
                        .foregroundColor(.white)
                        .font(Font.custom("Poppins-Medium", size: 18))
                    NavigationLink("Login", destination: SignInView())
                        .foregroundColor(.orange)
                        .font(Font.custom("Poppins-Medium", size: 18))
                }.offset(y: 100)
            }
            .frame(width: 350, height: 60)
        }.ignoresSafeArea()
    }
}

struct SignUpView_Previews: PreviewProvider {
    static var previews: some View {
        SignUpView().environmentObject(AppViewModel())
    }
}

Here's my AppViewModel:


import Foundation
import Firebase


class AppViewModel: ObservableObject {
    
    let auth = Auth.auth()
    @Published var signedIn = false
    
    var isSignedIn: Bool {
        // If it does NOT equal nil, then this means it is TRUE that, YES, we are indeed signed in. Yay!
        return auth.currentUser != nil
    }
    
    
    // If you want to pass information, you have to make placeholders for the variables to be passed in.
    func signUp(email: String, password: String) {
        auth.createUser(withEmail: email, password: password) { [weak self] result, error in
            guard result != nil, error == nil else {
                return
            }
            DispatchQueue.main.async {
                // Successfully signed up
                self?.signedIn = true
            }
        }
    }
    
    func login(email: String, password: String) {
        auth.signIn(withEmail: email, password: password) { [weak self] result, error in
            guard result != nil, error == nil else {
                return
            }
            
            DispatchQueue.main.async {
                // Successfully signed in
                self?.signedIn = true
            }
//            if error != nil {
//                print(error!.localizedDescription)
//            }
        }
    }
    
    func signOut() {
        try? auth.signOut()
        self.signedIn = false
        
    }
    
}

Please let me know if I need to include more code...? Thanks in advance! (I'm a newbie.)

Kiarra J.
  • 39
  • 7

2 Answers2

0

Figured it out for anyone interested...

NavigationLink("Login", destination: SignInView())

Needed to be changed to: NavigationLink("Sign Up", destination: SignUpView().environmentObject(self.model))

Kiarra J.
  • 39
  • 7
0

You need to initialize your AppViewModel() somewhere earlier in your stack, and then pass it to the view. You also need to add it to the preview to make it work (just like you did), but that will not make the actual code compile, just the preview.

As an example:

@main
struct TestApp: App {

    @StateObject var appViewModel = AppViewModel()

    var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(appViewModel)
        }
    }
}

struct ContentView: View {

    @EnvironmentObject var viewModel: AppViewModel

    var body: some View {
        VStack {
            if viewModel.loggedIn {
                Text("Logged in!")
            } else {
                Text("Not logged in!")
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
            .environmentObject(AppViewModel())
    }
}
bjorn.lau
  • 774
  • 5
  • 16