Auth.auth().currentUser?.uid = nil iOS Firebase - ios

Setting a variable to the value of Auth.auth().currentUser?.uid always returns nil even after the user has signed in, am I missing the fact that Firebase sometimes uses Async methods? The function used to return data from a Firebase realtime database is ran after the sign in function and is currently using a constant UID for testing purposes.
typealias RetrieveUserCompletionBlock = ((_ userType: String) -> Void)
func retrieveUserType(withBlock completion: #escaping RetrieveUserCompletionBlock){
let userTypeDB = Database.database().reference()
let currentUser = "kLxqZteRfBeC0bNIkLCjrPukMGx1"
var testUser = Auth.auth().currentUser?.uid
print(testUser)
userTypeDB.child("UserType").child(currentUser).observeSingleEvent(of: .value, with: {
(snapshot) in
// Get user value
let value = snapshot.value as? NSDictionary
let email = value?["Email"] as? String ?? ""
completion(value?["User Type"] as? String ?? "")
}){
(error) in
completion("default value")
print(error.localizedDescription)
}
}
#IBAction func loginButtonPressed(_ sender: Any) {
SVProgressHUD.show()
Auth.auth().signIn(withEmail: emailTextField.text!, password: passwordTextField.text!) { (user, error) in
if error != nil {
//error
if let errorCode = AuthErrorCode(rawValue: error!._code) {
switch errorCode {
case .missingEmail:
SVProgressHUD.showError(withStatus: "Please enter a email in the text field")
SVProgressHUD.dismiss(withDelay: 2)
case .userDisabled:
SVProgressHUD.showError(withStatus: "Your account is disabled")
SVProgressHUD.dismiss(withDelay: 2)
case .invalidEmail:
SVProgressHUD.showError(withStatus: "Invalid email, please enter a valid email")
SVProgressHUD.dismiss(withDelay: 2)
case .wrongPassword:
SVProgressHUD.showError(withStatus: "Incorrect password")
SVProgressHUD.dismiss(withDelay: 2)
case .userNotFound:
SVProgressHUD.showError(withStatus: "Account details not found, please try again")
SVProgressHUD.dismiss(withDelay: 2)
default:
print("Error")
}
}
}
else {
//success
SVProgressHUD.showSuccess(withStatus: "Success")
SVProgressHUD.dismiss(withDelay: 1)
self.performSegue(withIdentifier: "goToMenuFromLogin", sender: self)
}
}
retrieveUserType { (userType) in
if userType != "Student"{
print("error")
} else {
print("success")
}
}
}

Related

How to verify Email and password in xcode with firebase

I want to verify users that sign in with email and password with a verification email in firebase.
this is my code:
#IBAction func Login(_ sender: Any) {
guard let email = txtUser.text, email != "",
let password = txtPass.text, password != ""
else {
AlertController.showAlert(self, title: "Missing Info", message: "Please fill out all required fields")
return
}
Auth.auth().signIn(withEmail: txtUser.text!, password: txtPass.text!, completion: { (authResult,error) in
if error != nil{
AlertController.showAlert(self, title: "Error", message: error!.localizedDescription)
} else if authResult != nil {
self.performSegue(withIdentifier: "SegueMode", sender: self)
}
})
}
Have you tried this
Auth.auth().sendSignInLink(toEmail:email,
actionCodeSettings: actionCodeSettings) { error in
// ...
if let error = error {
self.showMessagePrompt(error.localizedDescription)
return
}
// The link was successfully sent. Inform the user.
// Save the email locally so you don't need to ask the user for it again
// if they open the link on the same device.
UserDefaults.standard.set(email, forKey: "Email")
self.showMessagePrompt("Check your email for link")
// ...
}
Here is Function that i used in my project
(Note: Change according to your requirement!)
func loginUser() {
guard let email = txtUserName.text, let password = txtPassword.text else { return }
Auth.auth().signIn(withEmail: email, password: password, completion: { (user, error) in
if let error = error {
print(error.localizedDescription)
self.lblSuccess.isHidden = false
self.lblSuccess.textColor = UIColor.red
self.lblSuccess.text = "Invalid Credential Please Check Your Email and password"
}else if let user = Auth.auth().currentUser {
let listVC = self.storyboard?.instantiateViewController(withIdentifier: "UserListVC") as! UserListVC
print(user)
self.lblSuccess.isHidden = false
self.lblSuccess.textColor = UIColor.green
self.lblSuccess.text = "Login SuccessFully!!"
self.navigationController?.pushViewController(listVC, animated: true)
}
})
}

Not finding usernames already in the Firebase Database with Swift

I am trying to check my Firebase database to see if there is already a username taken within the database. Unfortunately, when I use a username which is already saved in there, it doesn't let me know. Code below.
#objc func pushedToRegister(_ sender: SignInSignUpButtons) {
checkUserNameAlreadyExist()
Auth.auth().createUser(withEmail: email.text!, password: password.text!, completion: { (user, error) in
if error != nil {
self.errorMessage = (error?.localizedDescription)!
print(error!)
self.showErrorView(forReason: 0)
return
}
else {
print("Registration was successful!")
//Here we should go back to the home screen where the message at the top should say welcome back USER!
if let userID = user?.user.uid {
//Create User Profile
let databaseRef = Database.database().reference()
let usersRef = databaseRef.child("Users").child(userID)
let usernameValue = ["username":self.username.text]
usersRef.updateChildValues(usernameValue, withCompletionBlock: { (err, ref) in
if err != nil {
print(err!.localizedDescription)
self.showErrorView(forReason: 2)
return
}
//Profile created and updated!
self.navigationController?.popViewController(animated: true)
})
}
}
})
}
func checkUserNameAlreadyExist() {
let ref = Database.database().reference()
var usernameTaken = false
ref.child("Users").queryOrdered(byChild: "username").queryEqual(toValue: username.text!).observeSingleEvent(of: .value, with: { snapshot in
if snapshot.exists(){
usernameTaken = true
print("username taken")
}else{
usernameTaken = false
print("username available")
}
}) { error in
print(error.localizedDescription)
}
if usernameTaken == false{
//do stuff with unique username
}
}
Unfortunately, every time I type in the same username, it lets me create a new profile everytime. It does not alert me to the fact that the username is already taken.
Firebase functions are asynchronous, so they do not block the rest of the code from running. If you want your code to wait for a function to complete, one option is use closures. You can check out my blog post on closures to see some examples.
Also, the code shown doesn't actually do anything to prevent the rest of the function from running. You need to write some sort of condition to handle that. For example, you could use a boolean in your completion handler, like this:
func checkUserNameAlreadyExist(completion: #escaping (Bool) -> Void) {
let ref = Database.database().reference()
ref.child("Users").queryOrdered(byChild: "username").queryEqual(toValue: username.text!).observeSingleEvent(of: .value, with: { snapshot in
if snapshot.exists() {
usernameTaken = true
print("username taken")
completion(true)
} else {
usernameTaken = false
print("username available")
completion(false)
}
}) { error in
print(error.localizedDescription)
completion(true)
}
}
Then in pushToRegister, you check if the boolean is true before proceeding.
#objc func pushedToRegister(_ sender: SignInSignUpButtons) {
checkUserNameAlreadyExist() { isTaken in
if (isTaken == true) {
// show some message to the user
return
}
Auth.auth().createUser(withEmail: email.text!, password: password.text!, completion: { (user, error) in
if let error = error {
self.errorMessage = error.localizedDescription
print(error)
self.showErrorView(forReason: 0)
return
}
print("Registration was successful!")
//Here we should go back to the home screen where the message at the top should say welcome back USER!
if let userID = user?.user.uid {
//Create User Profile
let databaseRef = Database.database().reference()
let usersRef = databaseRef.child("Users").child(userID)
let usernameValue = ["username":self.username.text]
usersRef.updateChildValues(usernameValue, withCompletionBlock: { (err, ref) in
if let err = err {
print(err.localizedDescription)
self.showErrorView(forReason: 2)
return
}
//Profile created and updated!
self.navigationController?.popViewController(animated: true)
})
}
})
}
}

Authenticated User entry are not saved in Database

#objc func registerButtonClicked()
{
//After Firebase is configured this is called
guard let email = emailTextField.text else {
alertBox(title: "Error", message: "All fields are mandatory")
return }
guard let password = passwordTextField.text else { return }
guard let name = nameTextField.text else { return }
Auth.auth().createUser(withEmail: email, password: password) { (data, error) in
if error != nil{
print(error.debugDescription)
print("Error occurred")
} else
{
print("Data -- >\(String(describing: data))")
self.saveDataInFirebase(name: name, password: password, email: email)
//here the data is saved in authentication table in firebase so next step //was to save its detail in db in json
}
}
}
func saveDataInFirebase(name: String, password: String, email: String)
{
let userData = ["name" : name, "email" : email]
print("name \(name) --- Email \(email)")
// printing the details to be saved in DB
let firebaseRef = Database.database().reference(fromURL: "https://chatdemo1-d3423.firebaseio.com/")
firebaseRef.updateChildValues(userData) { (error, dbRef) in
if error != nil{
print("------------------")
print(error.debugDescription)
print("----------------------")
}
else
{
print("Data Saved Successfully")
}
}
}
The code in the question is probably not what you want to use as it will overwrite your entire Firebase each time it's run.
The reason for that is you're not writing data to a child node of your Firbase, you are writing it to the main node. This is what's being written:
your_firebase //<- this is where you're writing too each time
email: "some email"
name: "some name"
my guess is you want to stucture it like this
your_firebase
user_id
email: "some email"
name: "some name"
and the code to do that would be
var ref: DatabaseReference!
func viewDidLoad() {
self.ref = Database.database().reference()
.
.
.
}
func createUser() {
let email = "some email"
let password = "some password"
let name = "some name"
Auth.auth().createUser(withEmail: email, password: password) { (authResult, error) in
if let x = error { //some fancy error checking
let err = x as NSError
switch err.code {
case AuthErrorCode.emailAlreadyInUse.rawValue:
print("email in use!")
default:
print("unknown error")
}
} else {
guard let user = authResult?.user else { return }
let uid = user.uid
let usersRef = self.ref.child("users")
let thisUserRef = usersRef.child(uid)
thisUserRef.child("email").setValue(email)
thisUserRef.child("name").setValue(name)
}
}
}
This code assumes the Firebase Rules allows the user permission to write the the users node

Firebase observe method won't return and continue

I'm writing some code for a login page where we take a username and find the associated password. Temporarily I've said "if email exists under username, complete segue". However when I call the method getEmail which checks for email, it never seems to exit properly with a full email address. print(email) returns the right email address so I know I've retrieved it and it's correct. I never seem to make it out of the method though. Really stuck here! Heres my code:
func getEmail(name: String) -> String{
var email = ""
ref = Database.database().reference()
self.ref.child("Users").child(name).observeSingleEvent(of: .value, with: { (snapshot) in
if let user = snapshot.value as? [String:Any] {
print("email retrieved");
email = user["email"] as! String;
print(email)
return;
}
else{
print("email could not be retrieved from the user.");
}
}){ (error) in
print("Could not retrieve object from database because: ");
print((Any).self);
}
return email;
}
func validate(){
if(Username.text == ""){
EmptyStringAlert();
}
let email = getEmail(name: Username.text!);
print(email)
if(email == ""){
return;
}
performSegue(withIdentifier: "LoginSuccess", sender: nil)
}
The call to Firebase is asynchronous, so you have to use completion in your function to get the data. Try something like this:
func getEmail(name: String, completion: #escaping (Bool, Any?, Error?) -> Void) {
var email = ""
ref = Database.database().reference()
self.ref.child("Users").child(name).observeSingleEvent(of: .value, with: { (snapshot) in
if let user = snapshot.value as? [String:Any] {
email = user["email"] as! String
completion(true, email, nil)
}
else {
completion(false, nil, nil)
}
}){ (error) in
completion(false, nil, error)
}
}
func validate(){
if(Username.text == ""){
EmptyStringAlert();
}
getEmail(name: Username.text!) { (success, response, error) in
guard success, let email = response as? String else {
print(error ?? "Failed getEmail..")
return
}
if(email == "") {
return
}
performSegue(withIdentifier: "LoginSuccess", sender: nil)
}
}

How to write registered user into database in Swift?

When i select register.. the data is sent to Firebase authentication but does not store in the database? Can anyone tell me where im going wrong?
func handleRegister(){
// Validation
guard let email = emailTextField.text, let password = PassTextField.text, let name = nameTextField.text
else{
print("Please provide an Email and Password")
return
}
FIRAuth.auth()?.createUser(withEmail: email, password: password, completion: { (user: FIRUser?, error) in
if error != nil {
print(error!)
return
}
// Successfully authenticated user
// Saving Data to the Database
let ref = FIRDatabase.database().reference(fromURL: "https://chat-47e5b.firebaseio.com/")
let values = ["name": name, "email": email]
ref.updateChildValues(values, withCompletionBlock: { (err,ref)
in
if err != nil {
print(err!)
return
}
print("Saved user successfully into Firebase")
})
})
}
You are not doing it right, you should first get a reference to the db:
self.ref = FIRDatabase.database().reference()
Then:
let values = ["name": name, "email": email]
self.ref.child("users").child(user.uid).setValue(values)
As a side note, convert this:
if error != nil {
print(error!)
return
}
To this:
guard let error = error else {
print(error)
return
}

Resources