Firebase User Redirect - Auto Navigation for Logged-in Users
Complete guide to automatically redirect logged-in users to the main screen using Firebase authentication
Note:
This tutorial covers automatically redirecting logged-in users to the main screen, improving user experience by avoiding repeated login prompts. Make sure you have completed the Firebase integration, registration, and login setup from the previous tutorials.
Overview
Hello!
Requiring users who have previously logged into your app to enter their email and password every time can create negative results for user experience.
Firebase's ability to keep logged-in user information in memory using the "currentUser" resource makes the operations we'll perform much easier.
Reference:
Step 1: Determining Which Screen Logged-in Users Should Start With
First, decide which screen logged-in users should start with when entering the app.
Then, add an "Identifier" to the relevant screen in your Storyboard.
Step 2: SceneDelegate Implementation
Go to the SceneDelegate.swift file where user interface information is stored. As you can see in the func scene description section, it's written that the var window = UIWindow? variable defined above the class can be used in this area.
Inside the scene function, first assign the logged-in user information to a variable:
let currentUser = Auth.auth().currentUser
Then, if there's a logged-in user, we want the user to start with the ID shown in the image above.
Let's start with an if statement.
For this, first select the Storyboard file we'll work with, then assign which view to start from in this Storyboard to a variable.
The UIWindow variable called rootViewController will determine the app's opening screen. The opening screen will be equal to the variable we defined above, and the code block we'll get will be as follows:
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
let currentUser = Auth.auth().currentUser
if currentUser != nil {
// User is logged in - redirect to main screen
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let mainViewController = storyboard.instantiateViewController(withIdentifier: "MainViewController")
window?.rootViewController = mainViewController
} else {
// User is not logged in - show login screen
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let loginViewController = storyboard.instantiateViewController(withIdentifier: "LoginViewController")
window?.rootViewController = loginViewController
}
window?.makeKeyAndVisible()
}
This way, we will change the starting screen for logged-in users.
Step 3: Complete SceneDelegate Implementation
Here's the complete SceneDelegate.swift implementation:
import UIKit
import Firebase
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
window = UIWindow(windowScene: windowScene)
// Check if user is already logged in
let currentUser = Auth.auth().currentUser
if currentUser != nil {
// User is logged in - redirect to main screen
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let mainViewController = storyboard.instantiateViewController(withIdentifier: "MainViewController")
window?.rootViewController = mainViewController
} else {
// User is not logged in - show login screen
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let loginViewController = storyboard.instantiateViewController(withIdentifier: "LoginViewController")
window?.rootViewController = loginViewController
}
window?.makeKeyAndVisible()
}
func sceneDidDisconnect(_ scene: UIScene) {
// Called when the scene is being released by the system
}
func sceneDidBecomeActive(_ scene: UIScene) {
// Called when the scene has moved from an inactive state to an active state
}
func sceneWillResignActive(_ scene: UIScene) {
// Called when the scene will move from an active state to an inactive state
}
func sceneWillEnterForeground(_ scene: UIScene) {
// Called as the scene transitions from the background to the foreground
}
func sceneDidEnterBackground(_ scene: UIScene) {
// Called as the scene transitions from the foreground to the background
}
}
Step 4: Logout Functionality
When the user performs a logout operation, you can assign the following code line to a button:
Note: When you enter the logout code, Xcode may inform you that you need to perform this operation with a try command because you might encounter an error message here. This issue can be resolved with the do-try-catch trio as follows:
@IBAction func logoutButtonTapped(_ sender: Any) {
do {
try Auth.auth().signOut()
// Redirect to login screen after logout
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let loginViewController = storyboard.instantiateViewController(withIdentifier: "LoginViewController")
// Use a transition animation
UIView.transition(with: window!, duration: 0.3, options: .transitionCrossDissolve, animations: {
self.window?.rootViewController = loginViewController
}, completion: nil)
} catch {
showAlert(titleInput: "Error", messageInput: "Failed to logout")
}
}
Step 5: Enhanced User Experience
Smooth Transitions
Add smooth transitions between screens:
private func transitionToViewController(_ viewController: UIViewController) {
UIView.transition(with: window!, duration: 0.3, options: .transitionCrossDissolve, animations: {
self.window?.rootViewController = viewController
}, completion: nil)
}
Loading States
Show loading states during authentication checks:
private func showLoadingScreen() {
let loadingViewController = UIViewController()
loadingViewController.view.backgroundColor = .white
let activityIndicator = UIActivityIndicatorView(style: .large)
activityIndicator.center = loadingViewController.view.center
activityIndicator.startAnimating()
loadingViewController.view.addSubview(activityIndicator)
window?.rootViewController = loadingViewController
}
Step 6: Advanced Implementation
Multiple Screen Support
Handle different user roles and screens:
private func determineInitialScreen() -> UIViewController {
let currentUser = Auth.auth().currentUser
guard let user = currentUser else {
return instantiateViewController(withIdentifier: "LoginViewController")
}
// Check user role or preferences
if let userRole = UserDefaults.standard.string(forKey: "userRole") {
switch userRole {
case "admin":
return instantiateViewController(withIdentifier: "AdminViewController")
case "user":
return instantiateViewController(withIdentifier: "MainViewController")
default:
return instantiateViewController(withIdentifier: "MainViewController")
}
}
return instantiateViewController(withIdentifier: "MainViewController")
}
private func instantiateViewController(withIdentifier identifier: String) -> UIViewController {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
return storyboard.instantiateViewController(withIdentifier: identifier)
}
Persistent Login State
Check for persistent login state:
private func checkPersistentLogin() -> Bool {
// Check if user has chosen to stay logged in
let stayLoggedIn = UserDefaults.standard.bool(forKey: "stayLoggedIn")
if stayLoggedIn {
return Auth.auth().currentUser != nil
}
return false
}
Step 7: Error Handling
Network Issues
Handle network connectivity issues:
private func checkNetworkConnectivity() {
// Check if user has internet connection
if !isNetworkAvailable() {
// Show offline mode or cached data
showOfflineMode()
}
}
private func isNetworkAvailable() -> Bool {
// Implement network connectivity check
return true // Placeholder
}
private func showOfflineMode() {
// Show cached data or offline message
let offlineViewController = instantiateViewController(withIdentifier: "OfflineViewController")
window?.rootViewController = offlineViewController
}
Authentication State Changes
Listen for authentication state changes:
private func setupAuthStateListener() {
Auth.auth().addStateDidChangeListener { [weak self] (auth, user) in
DispatchQueue.main.async {
if user != nil {
// User signed in
self?.navigateToMainScreen()
} else {
// User signed out
self?.navigateToLoginScreen()
}
}
}
}
private func navigateToMainScreen() {
let mainViewController = instantiateViewController(withIdentifier: "MainViewController")
transitionToViewController(mainViewController)
}
private func navigateToLoginScreen() {
let loginViewController = instantiateViewController(withIdentifier: "LoginViewController")
transitionToViewController(loginViewController)
}
Note:
This implementation significantly improves user experience by automatically handling navigation based on authentication state. Remember to test thoroughly with different scenarios and user states before deploying to production.