Home > Software design >  SwiftUI NavigationLink doesn't trigger with onReceive method
SwiftUI NavigationLink doesn't trigger with onReceive method

Time:12-23

I'm trying to learn how to do basic login in swift with Firebase and it's causing me to lose my mind over navigating to the main page of the app once the login is complete. I have a ViewModel that manages the login, and in the view I have added an onReceive property to listen on a viewModel's boolean to detect sign in and trigger the navigation. I don't understand why it's not working, any help would be greatly appreciated!

ViewModel:

class LoginViewModel: ObservableObject {
    let auth = Auth.auth()
    var userInfo: User?
    
    @Published var isSignedIn = false
    
    func signIn(email: String, password: String) {
        auth.signIn(withEmail: email, password: password) { _, error in
            if let error = error as? NSError {
                print("Error happened on login" error.description)
            } else {
                print("Login successful")
                if let user = self.auth.currentUser {
                    print("We have a user")
                    self.userInfo = user
                    self.isSignedIn.toggle()
                }
            }
        }
    }
}

View:

struct LoginPage: View {
    @State var email = ""
    @State var password = ""

    @ObservedObject var viewModel = LoginViewModel()

    var body: some View {
        NavigationView {
            ZStack {
                VStack {
                    TextField("Email", text: $email).padding()
                        .background(Color(.secondarySystemBackground))
                        .padding()
                    TextField("Password", text: $password).padding()
                        .background(Color(.secondarySystemBackground))
                        .padding()
                    Button(action: {
                        guard !email.isEmpty, !password.isEmpty else {
                            return
                        }
                        viewModel.signIn(email: email, password: password)

                    }, label: {
                        Text("Sign in")
                            .padding(EdgeInsets(top: 12, leading: 35, bottom: 12, trailing: 35))
                            .foregroundColor(.white)
                            .background(Color.blue)
                            .cornerRadius(15)
                    })
                }
            }.navigationTitle("Login")
        }.onReceive(viewModel.$isSignedIn) { isSignedIn in
            if isSignedIn {
                print("ok damm")
                NavigationLink(destination: HomePage()) {
                    EmptyView()
                }
            }
        }
    }
}

Note that "ok damm" prints every time.

CodePudding user response:

Try the below code it will help you to solve your issue:

NavigationLink(destination: Text("Second View"), isActive: $isSignedIn){
                EmptyView()
            }

View:

struct LoginPage: View {
    @State var email = ""
    @State var password = ""

    @ObservedObject var viewModel = LoginViewModel()

    var body: some View {
        NavigationView {
            ZStack {
                VStack {
                    TextField("Email", text: $email).padding()
                        .background(Color(.secondarySystemBackground))
                        .padding()
                    TextField("Password", text: $password).padding()
                        .background(Color(.secondarySystemBackground))
                        .padding()
                    Button(action: {
                        guard !email.isEmpty, !password.isEmpty else {
                            return
                        }
                        viewModel.signIn(email: email, password: password)

                    }, label: {
                        Text("Sign in")
                            .padding(EdgeInsets(top: 12, leading: 35, bottom: 12, trailing: 35))
                            .foregroundColor(.white)
                            .background(Color.blue)
                            .cornerRadius(15)
                    })
                }

              NavigationLink(destination: HomePage()), isActive: $isSignedIn){
                EmptyView()
            }
            }.navigationTitle("Login")
        }
    }
}

CodePudding user response:

Answer by Jatin Bhuva works but is deprecated for iOS 16. Here's the solution for new iOS versions:

struct LoginPage: View {
    @State var email = ""
    @State var password = ""

    @ObservedObject var viewModel = LoginViewModel()

    var body: some View {
        NavigationStack {
            ZStack {
                VStack {
                    TextField("Email", text: $email).padding()
                        .background(Color(.secondarySystemBackground))
                        .padding()
                    TextField("Password", text: $password).padding()
                        .background(Color(.secondarySystemBackground))
                        .padding()
                    Button(action: {
                        guard !email.isEmpty, !password.isEmpty else {
                            return
                        }
                        viewModel.signIn(email: email, password: password)

                    }, label: {
                        Text("Sign in")
                            .padding(EdgeInsets(top: 12, leading: 35, bottom: 12, trailing: 35))
                            .foregroundColor(.white)
                            .background(Color.blue)
                            .cornerRadius(15)
                    })
                    Text("Don't have an account yet ?").padding(EdgeInsets(top: 20, leading: 0, bottom: 10, trailing: 0))
                    Button(action: {
                        guard !email.isEmpty, !password.isEmpty else {
                            return
                        }
                        viewModel.signUp(email: email, password: password)
                    }, label: {
                        Text("Create an account")
                            .padding(EdgeInsets(top: 12, leading: 35, bottom: 12, trailing: 35))
                            .foregroundColor(.white)
                            .background(Color.blue)
                            .cornerRadius(15)
                    })
                }
            }.navigationTitle("Login")
                .navigationDestination(isPresented: $viewModel.isSignedIn) {
                    HomePage()
                }
        }
    }
}

Notice how I replaced the NavigationView with a NavigationStack and added .navigationDestination(isPresented:) that listens for changes on my model's isSignedIn published property.

  • Related