Initial ViewController with Firebase bug fix

Viewed 66

I've strange bug I cannot understand how to solve. When I set which ViewController should be opened depends if users is registered and I put this code in SceneDelegate I get black screen for a moment before appearing ViewController

import UIKit
import FirebaseAuth

    class SceneDelegate: UIResponder, UIWindowSceneDelegate {
    
        var window: UIWindow?
        let storyBoard = UIStoryboard(name: "Main", bundle: nil)
    
    
        func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
            guard let windowScene = (scene as? UIWindowScene) else { return }
            window = UIWindow(frame: windowScene.coordinateSpace.bounds)
            window?.windowScene = windowScene
    
            if let user = Auth.auth().currentUser {
                FirestoreService.shared.getUserData(user: user) { (result) in
                    switch result {
                    case .success(let muser):
                        let navigationController = self.storyBoard.instantiateViewController(withIdentifier: "Navigation") as! UINavigationController
                        let conroller = self.storyBoard.instantiateViewController(withIdentifier: "MainController") as! MainController
                        navigationController.viewControllers = [conroller]
                        self.window?.rootViewController = navigationController
                    case .failure(_):
                        let conroller = self.storyBoard.instantiateViewController(withIdentifier: "SignUpController") as! SignUpController
                        self.window?.rootViewController = conroller
                    }
                }
            } else {
                print(3)
                let conroller = storyBoard.instantiateViewController(withIdentifier: "SignUpController") as! SignUpController
                self.window?.rootViewController = conroller
            }
            window?.makeKeyAndVisible()
        }

enter image description here

But when I put code for appearing ViewController outside the firebase it works as it should. How this can be fixed?

1 Answers

I think there are 2 ways to solve this issue.

1. Using a splash screen:

Don't make the decision of showing a login screen or main screen in scene(_:, session, connectionOptions). Instead, create a new view controller called SplashScreenViewController. Set your app's logo in its center. And then in its viewDidLoad() move your logic to check if the user is already logged in:

func chooseAndPresentStartScreen() {
    let storyBoard = UIStoryboard(name: "Main", bundle: nil)
    if let user = Auth.auth().currentUser {
        FirestoreService.shared.getUserData(user: user) { (result) in
            switch result {
            case .success(let muser):
                let navigationController = storyBoard.instantiateViewController(withIdentifier: "Navigation") as! UINavigationController
                let conroller = storyBoard.instantiateViewController(withIdentifier: "MainController") as! MainController
                navigationController.viewControllers = [conroller]
                self.present(navigationController, animated: true, completion: nil)
                
            case .failure(_):
                let conroller = storyBoard.instantiateViewController(withIdentifier: "SignUpController") as! SignUpController
                self.present(conroller, animated: true, completion: nil)
            }
        }
    } else {
        print(3)
        let conroller = storyBoard.instantiateViewController(withIdentifier: "SignUpController") as! SignUpController
        self.present(conroller, animated: true, completion: nil)
    }
}

2. Using User Defaults:

Define a global variable for the key used to save user default value.

let isUserLoggedInKey: String = "IsUserLoggedIn"

After the user logs-in:

let defaults = UserDefaults.standard
defaults.setValue(true, forKey: isUserLoggedInKey)

Then in your scene(_:, session, connectionOptions), check if this value is set.

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    guard let windowScene = (scene as? UIWindowScene) else { return }
    window = UIWindow(frame: windowScene.coordinateSpace.bounds)
    window?.windowScene = windowScene
    let defaults = UserDefaults.standard
    let isLoggedIn = defaults.bool(forKey: isUserLoggedInKey)
    if isLoggedIn {
        
        let navigationController = self.storyBoard.instantiateViewController(withIdentifier: "Navigation") as! UINavigationController
        let conroller = self.storyBoard.instantiateViewController(withIdentifier: "MainController") as! MainController
        navigationController.viewControllers = [conroller]
        self.window?.rootViewController = navigationController
        
    } else {
        let conroller = self.storyBoard.instantiateViewController(withIdentifier: "SignUpController") as! SignUpController
        self.window?.rootViewController = conroller
    }
    window?.makeKeyAndVisible()
    
}   

Don't forget to set isLoggedInKey to false when the user logs out.

let defaults = UserDefaults.standard
defaults.setValue(false, forKey: isUserLoggedInKey)

If you don't want to manually save value for isUserLoggedInKey when the user logs in and logs out, you can also use a state change listener. It takes a callback that is triggered when the user logs in or logs out. Add this in your Login view controller:

Auth.auth().addStateDidChangeListener { (auth, user) in 
    let defaults = UserDefaults.standard
    defaults.setValue(user != nil, forKey: isUserLoggedInKey)
}
Related