Search code examples
iosswiftauthenticationnavigationstoryboard

Swift: How do I prevent users from navigating back to the root viewController when they are not authenticated?


I have a root viewController that is the main viewController that users can navigate to when they have been authenticated. Right now, when the app loads the root viewController, it checks if the user is logged in and navigates to the login viewController if the user isn't authenticated.

The problem is I don't know how to prevent the user from returning to the main viewController without authenticating. Right now, they are able to return via back button if I use Show, or by gesture if I use Present Modally or Show Detail. I have searched for hours and haven't found the answer to this problem. It would be easy if I could just remove the root viewController instance with something like finish() like in Android, and then create a new one once the user logs in but I'm not sure if that's even possible. How can I solve this?

Here is the code that presents my LoginViewController:

override func viewDidLoad() {
    super.viewDidLoad()
        
    if (!isUserSignedIn()) {
        performSegue(withIdentifier: "pairedDevicesToLogin", sender: self)
    }
        
    navigationController?.navigationBar.isHidden = false
}

Solution

  • For anyone struggling with navigation flow for authentication in iOS this is what worked for me.

    With iOS 13 and later, authentication code should go in SceneDelegate instead of AppDelegate like this:

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
        // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
        // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
    
        guard let windowScene = (scene as? UIWindowScene) else { return }
            
        let window = UIWindow(windowScene: windowScene)
        let storyboard = UIStoryboard(name: "Main", bundle: nil)
            
        _ = Auth.auth().addStateDidChangeListener { (auth, user) in
            if ((user) != nil) {
                self.window?.rootViewController = storyboard.instantiateViewController(withIdentifier: "myMainViewController")
            } else {
                self.window?.rootViewController = storyboard.instantiateViewController(withIdentifier: "loginViewController")
                }
            } 
        }
    

    This will allow the app to decide what viewController to load first based off of the authentication results.