1
votes

I am logging in my users using Parse. As my app opens my LaunchVieWController determines whether users are already signed in or not:

override func viewDidLoad() {
    super.viewDidLoad()

    //Make sure cached users don't have to log in every time they open the app
    var currentUser = PFUser.currentUser()
    println(currentUser)
    if currentUser != nil {
        dispatch_async(dispatch_get_main_queue()) {
            self.performSegueWithIdentifier("alreadySignedIn", sender: self)
        }
    } else {
        dispatch_async(dispatch_get_main_queue()) {
            self.performSegueWithIdentifier("showSignUpIn", sender: self)
        }
    }
}

If users are already signed in they are taken to the table view described below. If they are not logged in, they are taken to a view in which they can go to the signup view or the login view.

My signup works perfectly fine, and signs up users and then redirects them to the table view. Here is the signup function (it is in a separate controller from the login function):

func processSignUp() {

    var userEmailAddress = emailAddress.text
    var userPassword = password.text

    // Ensure username is lowercase
    userEmailAddress = userEmailAddress.lowercaseString

    // Add email address validation

    // Start activity indicator
    activityIndicator.hidden = false
    activityIndicator.startAnimating()

    // Create the user
    var user = PFUser()
    user.username = userEmailAddress
    user.password = userPassword
    user.email = userEmailAddress

    user.signUpInBackgroundWithBlock {
        (succeeded: Bool, error: NSError?) -> Void in
        if error == nil {

            dispatch_async(dispatch_get_main_queue()) {
                self.performSegueWithIdentifier("signInToNavigation", sender: self)
            }

        } else {

            self.activityIndicator.stopAnimating()

            if let message: AnyObject = error!.userInfo!["error"] {
                self.message.text = "\(message)"
            }               
        }
    }
}

My login function looks like so:

@IBAction func signIn(sender: AnyObject) {
    var userEmailAddress = emailAddress.text
    userEmailAddress = userEmailAddress.lowercaseString
    var userPassword = password.text

   PFUser.logInWithUsernameInBackground(userEmailAddress, password:userPassword) {
        (user: PFUser?, error: NSError?) -> Void in
        if user != nil {
            dispatch_async(dispatch_get_main_queue()) {
                self.performSegueWithIdentifier("signInToNavigation", sender: self)
            }
        } else {
            if let message: AnyObject = error!.userInfo!["error"] {
                self.message.text = "\(message)"
            }
        }
    }
}

After users log in they are sent to a table view. In that table view I am trying to access the current user object in the following function, as I want to load different data based on who the user is:

override func queryForTable() -> PFQuery {
    var query = PFQuery(className:"MyClass")
    query.whereKey("userID", equalTo: PFUser.currentUser()!)
    return query
}

When I try to log in with a user I get thrown the following error: "fatal error: unexpectedly found nil while unwrapping an Optional value". It seems like the PFUser.currentUser() object is nil.

When users are sent to the table view after signing up the PFUser.currentUser() object is set and works perfectly.

My guess is that this is because of the fact that the PFUser.logInWithUsernameInBackground is happening in the background and that my query is trying to get the PFUser.currentUser() object before it has been loaded. Is there anyway I can work around this issue? In the table view the value of PFUser.currentUser() is needed to load the table data. Can I somehow make sure that PFUser.currentUser() gets assigned with the current user object before the function gets called (for example, by not loading in users in the background thread)?

All help is much appreciated!

EDIT: I've updated this post with some more of my code to help highlight any bug that I may be missing.

2
Have you tested if it's called before the login response handler? - Wain
I'm not entirely sure how to test for this, but I've added a breakpoint here: i.imgur.com/cYZLtNj.png and the error persists (It doesn't stop at the breakpoint but throws an the same error as before in the table view). This seems really odd as I thought the signInToNavigation segue would have to execute before any code on in that view would execute. - user3753098
It would, unless there is a different way to get to it. Logging is perhaps a better potion than breakpoints at this stage - Wain
I've updated my post with more (hopefully) relevant code. When I sign up users the PFUser.currenUser object gets set and everything works. Can't wrap my head around why this is not working with the login function though. - user3753098
You should really use the form if let user = user { instead of if user != nil { to check that the user is available, but other than that I can't see anything wrong. Have you logged the returned user and error in the login response? - Wain

2 Answers

3
votes

I discovered that this problem appeared because the segue signInToNavigation was wired from the Login-button, instead from the login view controller itself. This resulted in the segue being executed before the login function was executed, and therefore the PFUser.currentUser()object was not yet assigned when the table view loaded. I solved this by rewiring the segues. Stupid slip-up on my side.

Thanks to Ryan Kreager and Wain for taking time to help me figure out this issue!

0
votes

You can try checking if PFUser.currentUser() != nil instead to make sure it's being set right after login. If it's not being set right off the bat, you know there is a deeper login problem.

Also, try removing the dispatch_async(dispatch_get_main_queue()){ wrapper around your call to the segue.

It's unnecessary (logInWithUsernameInBackground already returns to the main thread) and I have a hunch that it's creating a racing condition where the local object is not being set first because Parse can't do any post-call cleanup since you're going right for the main thread.