2

I am having trouble getting data to load correctly in my ViewDidAppear method when using a Firebase login. I want the user to press the login button, the app to retrieve the user's information from Firebase, and then display it in the next view controller (which is part of a new tab bar controller).

When I use breakpoints, I can see that the new view appears and THEN the data is pulled from Firebase. I need the data to be loaded from Firebase before the view appears.

Here is my code for the login button:

let userSettings = NSUserDefaults.standardUserDefaults()

@IBAction func signInButton(sender: AnyObject) {

    ref.authUser(emailTextField.text, password: passwordTextField.text) { (error, authData) -> Void in

        if error != nil {

            let accountError = UIAlertController(title: "Error logging in", message: "Please try again", preferredStyle: .Alert)
            accountError.addAction(UIAlertAction(title: "Ok", style: .Default, handler: nil))
            self.presentViewController(accountError, animated: true, completion: nil)


        }  else {                

            userID = ref.authData!.uid

            userSettings.setObject(userID!, forKey: "firebaseUserID")

            let playNumberRef = Firebase(url:"https://(removed for security).firebaseIO.com/users/\(userID!)/playNumber")


            //Check Firebase for the user's last playnumber and set it in the user defaults

            playNumberRef.observeSingleEventOfType(.Value, withBlock: { snap in

                //Convert type AnyObject to NSNumber so that we can check its value

                if let checkPlay = snap.value as? NSNumber {

                    userSettings.setInteger(Int(checkPlay), forKey: "playNumber")

                }})

Here is my code in the viewWillAppear method:

var playNumber = userSettings.integerForKey("playNumber")

dayCounterLabel.text = "Day \(playNumber)"

When the view first appears, the dayCounterLabel.text has not been updated. If I navigate to a different tab and then navigate back, it updates.

How can get the data to save into the user defaults prior to the view appearing?

2
  • I forgot to mention that to initiate the next view controller I am using the following code: let vc = self.storyboard!.instantiateViewControllerWithIdentifier("autoLogin") as! UITabBarController vc.selectedIndex = 0 self.presentViewController(vc, animated: true, completion: nil) Commented Apr 16, 2016 at 19:22
  • why didn't you hide the label view by default and show it when the data from Firebase is pulled in your closure? Commented Apr 16, 2016 at 21:14

2 Answers 2

2

This is a common issue:

Firebase is asynchronous and viewWillAppear is occurring before Firebase has had time to return the data. This code

 playNumberRef.observeSingleEventOfType

is a block and the app should not proceed to show the next view until that block has the returned data.

In other words, show the next view from inside that block

I don't know how the rest of your code is set up, but here's a quickie example (untested so don't copy paste)

Suppose we have a loginViewController that lets the user login, and then a mainViewController for the main app view after the user logs in.

In the AppDelegate we have

func showMainView() {

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

    self.mainVC = (storyboard.instantiateControllerWithIdentifier("MainViewController") as? NSViewController)!

    self.mainVC.showWindow(self)

}

so in your case the block could be modified thusly:

playNumberRef.observeSingleEventOfType(.Value, withBlock: { snap in

   if let checkPlay = snap.value as? NSNumber {

     userSettings.setInteger(Int(checkPlay), forKey: "playNumber")

     let appDelegate = NSApplication.sharedApplication().delegate as! AppDelegate

     appDelegate.showMainView() //prepare to sho the main view

     self.view.window?.close() //get rid of this one

}})

So the main view controller is only displayed when Firebase has had a time to return the data and the vars are populated.

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you!! Moving the code inside of the block worked perfectly!
0
 if let checkPlay = snap.value as? NSNumber {
     dispatch_async(dispatch_get_main_queue(),{
        userSettings.setInteger(Int(checkPlay), forKey: "playNumber")
     })
 }})

Try this, it can help you.

1 Comment

For whatever reason, this solution didn't have any effect on the problem. Maybe it's because in the Firebase documentation it says "Value events are always triggered last and are guaranteed to contain updates from any other events which occurred before that snapshot was taken."??

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.