Type 'AppViewModel.Type' cannot conform to 'ObservableObject'

Howdy folks, hoping this is the right place for this. Fairly new to Xcode and swift. I've resolved all of my error but the one stated in the title. This (should be ) the last error until a successful build. Was told beginner questions welcome so here goes it.

Important Specs:
Mac mini M1
Xcode Version 12.5.1 (12E507)
Mac OS 12.0 Beta (21A5284e)

Background: I am trying to use Firebase SDK framework (installing from a GIT repo via swift package manager. All went well. Followed a guide where a guy had created a UI app with Firebase Auth however he used pods (I stayed away from pods as researching installing pods for a M1 device (every single forum gave VERY different methods) I just scrapped the pods for now.

Anyways In my Target swift file I am calling ContentView like so
I know my naming scheme is not correct I'd just like to get a successful build and then go back and fix all of the names. (Author of guide did not post his project )

Appreciate any help ! If there is anything I can upload to help please let me know.
I suspect it is with one of the /@EnvironmentObject/ instances.

import SwiftUI
import Firebase
@main
struct SwiftUILoginApp: App {
   @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
    var body: some Scene {
        WindowGroup {
            let ViewModel = AppViewModel
                ContentView() -->Type 'AppViewModel.Type' cannot conform to 'ObservableObject'
                    .environmentObject(ViewModel)

Of course I have read several forums where that specific error message may be misleading (ugh)

So here is the ObservableObject from the contentview swift file.
(This is all from a guide so any questions as to why it is done a certain way I do not know)

content file:

import SwiftUI
import FirebaseAuth

class AppViewModel: ObservableObject {
    
    let auth = Auth.auth()
    @Published var SignedIn = false
    
    var isSignedIn: Bool {
        return auth.currentUser != nil
    }
    
    func SignIn  (email: String, pswd: String){
        auth.signIn(withEmail: email, password: pswd) { [weak self ]result, error in
            guard result != nil, error == nil else {
                return
            }
            DispatchQueue.main.sync {
                self?.SignedIn = true
            }
        }
    }
    func SignUp  (email: String, pswd: String){
        auth.createUser(withEmail: email, password: pswd) { [weak self] result, error in
            guard result != nil, error == nil else {
                return
            }
            DispatchQueue.main.sync {
                self?.SignedIn = true
            }
        }
    }
    
    func Signout() {
        try? auth.signOut()
        self.SignedIn = false
        
    }
}
        struct ContentView: View {
            @EnvironmentObject var viewModel: AppViewModel
            
            var body: some View {
                NavigationView {
                    if viewModel.SignedIn {
                        VStack{
                            Text("You  are signed in")
                            Button(action: {
                                
                                viewModel.Signout()
                                
                            }, label: {
                                Text("Sign Out")
                                    .background(Color.green)
                                    .foregroundColor(/*@START_MENU_TOKEN@*/.blue/*@END_MENU_TOKEN@*/)
                                    .frame(width: 200, height: 50, alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/)
                                    .padding()
                            })
                    }
                    
                    
                    
                    
                }
                    else {
                        SigninView()
                    }
                
                }
                .onAppear {
                    viewModel.SignedIn = viewModel.isSignedIn
            }
        }
        
        
        
        struct SigninView: View {
            
            @State var email = ""
            @State var pswd = ""
            @EnvironmentObject var viewModel: AppViewModel
            
            var body: some View{
                VStack {
                    Image("logo")
                        .resizable()
                        .scaledToFit()
                        .frame(width: 500, height: 500)
                    
                    VStack{
                        TextField("Email Address", text: $email)
                            .disableAutocorrection(true)
                            .autocapitalization(/*@START_MENU_TOKEN@*/.none/*@END_MENU_TOKEN@*/)
                            .padding()
                            .frame(width: 400.0, height: 50.0)
                            .background(Color(.secondarySystemBackground))
                        SecureField("Password", text: $pswd)
                            .disableAutocorrection(true)
                            .autocapitalization(/*@START_MENU_TOKEN@*/.none/*@END_MENU_TOKEN@*/)
                            .padding(/*@END_MENU_TOKEN@*/)
                            .frame(width: 400.0, height: 50.0)
                            .background(Color(.secondarySystemBackground))
                        
                        Button(action: {
                            
                            guard !email.isEmpty,!pswd.isEmpty else  {
                                return
                            }
                            viewModel.SignIn(email: email, pswd: pswd)
                        }, label: {
                            Text("Sign In")
                                .padding()
                                .frame(width: 200.0, height: 50.0)
                                .background(Color.blue)
                                .cornerRadius(20.0)
                        })
                    }
                    .padding()
                    Spacer()
                }
                .navigationTitle("Sign In")
            }
        }
    }
    
    struct SignUpView: View {
        
        @State var email = ""
        @State var pswd = ""
        @EnvironmentObject var viewModel: AppViewModel
        
        var body: some View{
            VStack {
                Image("logo")
                    .resizable()
                    .scaledToFit()
                    .frame(width: 500, height: 500)
                
                VStack{
                    TextField("Email Address", text: $email)
                        .disableAutocorrection(true)
                        .autocapitalization(/*@START_MENU_TOKEN@*/.none/*@END_MENU_TOKEN@*/)
                        .padding()
                        .frame(width: 400.0, height: 50.0)
                        .background(Color(.secondarySystemBackground))
                    SecureField("Password", text: $pswd)
                        .disableAutocorrection(true)
                        .autocapitalization(/*@START_MENU_TOKEN@*/.none/*@END_MENU_TOKEN@*/)
                        .padding(/*@END_MENU_TOKEN@*/)
                        .frame(width: 400.0, height: 50.0)
                        .background(Color(.secondarySystemBackground))
                    
                    Button(action: {
                        
                        guard !email.isEmpty, !pswd.isEmpty else  {
                            return
                        }
                        viewModel.SignUp(email: email, pswd: pswd)
                    }, label: {
                        Text("Create Account")
                            .padding()
                            .frame(width: 200.0, height: 50.0)
                            .background(Color.blue)
                            .cornerRadius(20.0)
                    })
                    NavigationLink("Create Account", destination: SignUpView())
                }
                .padding()
                Spacer()
            }
            .navigationTitle("Create Account")
        }
    }

    struct ContentView_Previews: PreviewProvider{
        static var previews: some View{
            ContentView()
            // .preferredColorScheme(.dark)
        }
    }

Based on the code you've shown, your problem is in this line of code:

            let ViewModel = AppViewModel

Your environment should contain an instance of AppViewModel, but you've attempted to put the AppViewModel type into the environment.

An instance of AppViewModel of course would have type 'AppViewModel' which conforms to ObservableObject.

The type AppViewModel has type 'AppViewModel.Type' which doesn't conform to ObservableObject.

Once you've distinguished between those two things, you'll realize that the error message is exactly correct, although it didn't help you much in this case.

Possibly what you meant is:

            let viewModel = AppViewModel()

which creates an instance of AppViewModel. Note that it's unusual in Swift to capitalize variable names, although that might be a convention you've used in other languages. Capitalizing only types, and not variables or properties, helps to avoid accidentally confusing them.

1 Like

Dude, thank you for that. I knew it was something small and needed a swiftier set of eyes to look at it! Appreciate the advice too

Well it successfully built and ran (sorta? the simulator and the Canvas preview was way off even though they was both the same devices) and now when I try to run Xcode project crashes with error message
Screen Shot 2021-07-18 at 13.12.23 PM

You need to inject an instance of AppViewModel into the environment of your ContentView_Previews using the environmentObject modifier. For example:

struct ContentView_Previews: PreviewProvider{
    static var previews: some View {
        ContentView()
            .environmentObject(AppViewModel())
    }
}

that is very helpful. I hope this post helps other noobies too.

I hope y'all have an awesome week. :relaxed:

Note that in general case, creating instances of ObservableObject's inside body getter is a bad idea. Getter is called by the framework, and as a user you have little control of when it will be called. It may be called when you don't expect it, and your state in the AppViewModel will be reset. Better to store it in the StateObject. This will give you control on when AppViewModel is initialised.

@main
struct SwiftUILoginApp: App {
   @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
   @StateObject var viewModel = AppViewModel()
    var body: some Scene {
        WindowGroup {
                ContentView()
                    .environmentObject(viewModel)
        }
    }
}