cosmos cosmos - 3 months ago 14
iOS Question

Swift - Login with FireBase - Not being able to retrieve user data after login

Im trying to retrieve the users email after sign in (logincontroller) and display it in a UILABEL on another controller (maincontroller). When you create a user or use an existing one it works fine the first time after launching the app, but when you sign out and try to use another email it does not work.

I have two view controllers:

import UIKit
import Firebase
import FirebaseAuth
import FirebaseDatabase

class LoginViewController: UIViewController {

var ref: FIRDatabaseReference!


@IBOutlet weak var emailTextField: UITextField!
@IBOutlet weak var passwordTextField: UITextField!


override func viewDidLoad() {
super.viewDidLoad()

ref = FIRDatabase.database().reference()

}

override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}

@IBAction func signInButton(sender: AnyObject) {
FIRAuth.auth()?.signInWithEmail(emailTextField.text! , password: passwordTextField.text!, completion: { (user, error) in

if error != nil {
print(error!.localizedDescription)
}
})

self.performSegueWithIdentifier("MainViewSegue", sender: self)
}

@IBAction func createAccountButton(sender: AnyObject) {
FIRAuth.auth()?.createUserWithEmail(emailTextField.text!, password: passwordTextField.text!, completion: { (user, error) in

if error != nil {
print(error!.localizedDescription)
} else {

print("User Created.")
let userID: String = user!.uid
let userEmail:String = self.emailTextField.text!
self.ref.child("users").child(userID).setValue(["email": userEmail])
}
})
}
}


and:

import Foundation
import UIKit
import Firebase
import FirebaseDatabase

class MainViewController: UIViewController {

var ref: FIRDatabaseReference!
var refHandle: UInt!

@IBOutlet weak var userEmailLabel: UILabel!

override func viewDidLoad() {

super.viewDidLoad()

ref = FIRDatabase.database().reference()

refHandle = ref.observeEventType(FIRDataEventType.Value, withBlock: { (snapshot) in
let dataDict = snapshot.value as! [String: AnyObject]

print((dataDict))
})


let userID: String = (FIRAuth.auth()?.currentUser?.uid)!
ref.child("users").child(userID).observeSingleEventOfType(.Value, withBlock: { (snapshot) in
let userEmail = snapshot.value!["email"] as! String
self.userEmailLabel.text = userEmail
})
}

@IBAction func signOutButton(sender: AnyObject) {
try! FIRAuth.auth()!.signOut()
if let storyboard = self.storyboard {
let viewController = storyboard.instantiateViewControllerWithIdentifier("LoginViewController")
self.presentViewController(viewController, animated: false, completion: nil)
}
}
}


This is where I get the error EXC_BAD_INSTRUCTION:

let userID: String = (FIRAuth.auth()?.currentUser?.uid)!

Answer

CMD+CLICK on signInWithEmail in FIRAuth.auth()?.signInWithEmail(emailT... and you will be directed to its documentation, above that function in its documentation you will see :-

@param completion Optionally; a block which is invoked when the sign in flow finishes, or is canceled. Invoked asynchronously on the main thread in the future.

which means your completionBlock is invoked when your user is either signed in or signInWithEmail function has given some error.But in your case self.performSegueWithIdentifier("MainViewSegue", sender: self) will get called even before your completionBlock: is called.

Try this:-

  @IBAction func signInButton(sender: AnyObject) {
    FIRAuth.auth()?.signInWithEmail(emailTextField.text! , password: passwordTextField.text!, completion: { (user, error) in

        if error != nil {
            print(error!.localizedDescription)
        }else if error == nil{
                 self.performSegueWithIdentifier("MainViewSegue", sender: self)
          }
    })
}

Also replace :-

let userID: String = (FIRAuth.auth()?.currentUser?.uid)!

with

let userID: String = FIRAuth.auth()!.currentUser!.uid

No need to forcefully unwrap an optional value which you know does exist