Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Navigate after successful login with SwiftUI

I'm using Firebase and SwiftUI to log in as a user. I have everything working but can't figure out how to navigate to the next page once the user is successfully logged in. NavigationLinks seem weird to me so I'm wondering if there's any other way to navigate to the next view. The LoginView contains the login screen and the LoginViewModel does the sign-in procedure with Firebase. The issue is that if I return views based on a variable in the root view, it doesn't change the view once any view other than the root view is opened (i.e if I navigate to the Login view, it won't go to the main view after the login button is pressed).

ContentView (The root view displaying all the other views):

import SwiftUI

struct ContentView: View {
    @StateObject var userLoggedIn = LoginViewModel()
    var body: some View {
        if !userLoggedIn.isLoggedIn {
            LoginView()
        }
        else {
            MainView()
        }
    }
}

LoginView:

import SwiftUI
import FirebaseAuth

struct LoginView: View {
    
    @State private var email: String = ""
    @State private var password: String = ""
    @State private var isEditing = false
    @State private var showPassword = false
    @State private var radius = 300
    
    private var canLogIn: Bool {
        return !email.isEmpty && !password.isEmpty
    }

    let loginView = LoginViewModel()
    
    @ViewBuilder
    var body: some View {
        
        return NavigationView(content: {
            
            VStack {
                Spacer()
                    .frame(height: 150)
                
                Text("PET SUPPORT").foregroundColor(Color.petSupportText)
                    .font(Font.custom("Permanent Marker", size: 36))
                    .padding()

                Group {
                    
                    HStack {
                        
                        Text("EMAIL")
                            .font(Font.custom("Permanent Marker", size: 18))
                            .padding(.top, 10)
                        
                        Spacer()
                        
                    }
                    
                    TextField("Email", text: $email) {
                        isEditing in self.isEditing = isEditing
                    }
                    .autocapitalization(.none)
                    .keyboardType(.emailAddress)
                    .disableAutocorrection(true)
                    .padding(.top, 20)
                    
                    Divider()
                        .foregroundColor(.black)
                    
                }

                
                Group {
                    
                    HStack {
                        
                        Text("PASSWORD")
                            .font(Font.custom("Permanent Marker", size: 18))
                            .padding(.top, 10)
                        
                        Spacer()
                        
                    }

                    ZStack {
                        if showPassword {
                            TextField("Password", text: $password)
                        }
                        else {
                            SecureField("Password", text: $password)
                        }
                    }
                    .frame(height: 20)
                    .autocapitalization(.none)
                    .overlay(Image(systemName: showPassword ? "eye.slash" : "eye").onTapGesture { showPassword.toggle() }, alignment: .trailing)
                    .disableAutocorrection(true)
                    .padding(.top, 20)
                    
                    Divider()
                        .foregroundColor(.black)
                    
                }
                
                Spacer();
                
                Group {
                    
                    Button(action: {
                            loginView.login(email: email, password: password)
                            radius = 2000
                            MainView()
                    }, label: {
                        Text("Login")
                    })
                        .foregroundColor(.white)
                        .font(Font.custom("Permanent Marker", size: 18.0))
                        .padding(.horizontal, 20)
                        .padding()
                        .background(Color.petSupportBlue)
                        .cornerRadius(70.0)
                        .disabled(!canLogIn)
                }
                
                Spacer()
            }
            .padding(.horizontal, 30)
            .ignoresSafeArea()
            
        })
        
    }
}

LoginViewModel:

import Foundation
import Firebase

class LoginViewModel: ObservableObject {
    
    @Published var isLoggedIn = false
    func login(email: String, password: String) {
        
        Auth.auth().signIn(withEmail: email, password: password) { (result, error) in
            
            if let error = error {
                print(error.localizedDescription)
            } else {
                print("Logged In!")
                self.isLoggedIn = true
            }
        }
        
    }
    
}

MainView:

import SwiftUI

struct MainView: View {
    var body: some View {
        Text("Hello, World!")
    }
}
like image 650
Dayem Saeed Avatar asked Oct 21 '25 00:10

Dayem Saeed


1 Answers

If you don't want to deal with NavigationLink, as @Paulw11 mentioned in the comments, you can conditionally display a logged in/logged out view based on a property you set on an ObservableObject. You can even add animations/transitions. See below for a simple example:


class LoginManager : ObservableObject {
    @Published var isLoggedIn = false
    
    func login() {
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
            withAnimation {
                self.isLoggedIn = true
            }
        }
    }
}

struct ContentView : View {
    @StateObject var loginManager = LoginManager()
    
    var body: some View {
        if loginManager.isLoggedIn {
            LoggedInView()
                .frame(maxWidth: .infinity, maxHeight: .infinity)
                .transition(.move(edge: .leading))
        } else {
            LoginView(loginManager: loginManager)
                .frame(maxWidth: .infinity, maxHeight: .infinity)
                .transition(.move(edge: .leading))
        }
    }
}

struct LoginView : View {
    @ObservedObject var loginManager : LoginManager
    
    var body: some View {
        Button("Login") {
            loginManager.login()
        }
    }
}

struct LoggedInView : View {
    var body: some View {
        Text("Logged in!")
    }
}

I might also consider, if I were you, using at authStateListener and setting the logged in property based on that. That way, if the app is re-opened and the user is still logged in, they'll be transitioned to the logged-in page automatically.

like image 128
jnpdx Avatar answered Oct 22 '25 16:10

jnpdx



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!