Tom
Tom

Reputation: 4033

Firebase & Swift - delay change of ViewController until data is read

I have implemented a login to load data from a real-time database every time a user logs in. When the user has successfully logged in, the data is being read - the problem is that AppDelegate changes the ViewController faster than the data is being read. Therefore, nothing will be displayed until the app is closed again.

Is there a way to delay the change of the view until the data is read?

didFinishLaunchingOptions() inside AppDelegate:

let authListener = Auth.auth().addStateDidChangeListener { auth, user in

    let storyboard = UIStoryboard(name: "Main", bundle: nil)

    if user != nil {
        let controller = storyboard.instantiateViewController(withIdentifier: "RootPageViewController") as! RootPageViewController
        self.window?.rootViewController = controller
        self.window?.makeKeyAndVisible()
    } else {
        let vc = storyboard.instantiateViewController(withIdentifier: "SettingsTableViewController") as! SettingsTableViewController
        vc.runResetData()

        let controller = storyboard.instantiateViewController(withIdentifier: "ProfileViewController") as! ProfileViewController
        self.window?.rootViewController = controller
        self.window?.makeKeyAndVisible()
    }
}

LoginViewController:

@IBAction func login(_ sender: Any) {
    guard let email = emailInput.text else { return }
    guard let password = passwordInput.text else { return }

    Auth.auth().signIn(withEmail: email, password: password) { user, error in
        if error == nil && user != nil {
            self.readData()
            self.dismiss(animated: false, completion: nil)
        } else {
            print("Error logging in: ", error!.localizedDescription)

            let alert = UIAlertController(title: "Fehler beim Einloggen", message: error!.localizedDescription, preferredStyle: .alert)
            alert.addAction(UIAlertAction(title: "Verstanden", style: .default, handler: nil))
            self.present(alert, animated: true, completion: nil)
        }
    }
} 

func readData() {
    guard let uid = Auth.auth().currentUser?.uid else { return }
    let databaseRef = Database.database().reference()

    databaseRef.child("users").child(uid).observeSingleEvent(of: .value, with: { (snapshot) in

        let value = snapshot.value as? NSDictionary
        let monthlyStartKmData = value?["monthlyStartKm"] as? Int ?? 0
        let currentKmData = value?["currentKm"] as? Int ?? 0
        ...


        startKm = monthlyStartKmData
        currentKm = currentKmData
        ...


    }) { (error) in
        print(error.localizedDescription)
    }

}

Upvotes: 2

Views: 152

Answers (2)

Tom
Tom

Reputation: 4033

There's a possibility to delay the change of view until Firebase was able to load its data:

databaseRef.observeSingleEvent

Working code:

let authListener = Auth.auth().addStateDidChangeListener { auth, user in
            let databaseRef = Database.database().reference()

            let storyboard = UIStoryboard(name: "Main", bundle: nil)

            if user != nil {
                databaseRef.observeSingleEvent(of: .value, with: { snap in
                    let controller = storyboard.instantiateViewController(withIdentifier: "RootPageViewController") as! RootPageViewController
                    self.window?.rootViewController = controller
                    self.window?.makeKeyAndVisible()
                })
            } else {
                let vc = storyboard.instantiateViewController(withIdentifier: "SettingsTableViewController") as! SettingsTableViewController
                vc.runResetData()

                let controller = storyboard.instantiateViewController(withIdentifier: "ProfileViewController") as! ProfileViewController
                self.window?.rootViewController = controller
                self.window?.makeKeyAndVisible()
            }

        }

Upvotes: 0

Shehata Gamal
Shehata Gamal

Reputation: 100503

No you can't as the call to listen is asynchronous so it's run with a delay while the rootVC is setted , you need to show A loader like

 let vc = UIViewController()
 vc.view.backgroundColor = .red
 self.window?.rootViewController = vc
 self.window?.makeKeyAndVisible()    
 let authListener = Auth.auth().addStateDidChangeListener { auth, user in 
   .....
 }

Inside AppDelegate

if FIRAuth.auth().currentUser != nil {
   // go to home , if you went before you should have km month values cached 
}
else {
  // go to login
}

Upvotes: 2

Related Questions