How to update user information in firebase? - ios

i have this function to change user email. I'm able to change the email and i can see it in firebase console, but when i go return to my application to see user info, i see only the old email presented.
#IBAction func saveButton(_ sender: Any) {
let currentUser = Auth.auth().currentUser
currentUser?.updateEmail(to: emailTextField.text!) { error in
if let error = error {
print(error)
} else {
self.userDocRef = self.db.collection("users").document(self.auth.currentUser!.uid)
self.userDocRef?.getDocument(completion: {(snapshot, error) in
guard snapshot != nil else { print("Error:", error!); return }
let userData = snapshot!.data()!
self.emailTextField.text = userData["email"]! as? String
})
print("CHANGED")
}
}
}

Related

How can I delete a Firestore field, using whereField?

I'm trying to delete a field within a document, when the field "uid" matches the Current User's ID. I'm pretty stuck on this, and would appreciate any help. I detail, below, my code and how my database is set up.
#IBAction func deleteAccountButtonIsTapped(_ sender: Any) {
let db = Firestore.firestore()
let userID = Auth.auth().currentUser?.uid
let username = usernameTextField.placeholder
Auth.auth().currentUser?.delete(completion: { (error) in
if error != nil {
print("ERROR MAIN SETTINGS 136")
} else {
db.collection("FollowerList").whereField("uid", isEqualTo: userID!).getDocuments { (snapshot, error) in
for snapshot in snapshot?.documents {
}
}
}
}
)}
My Database has a collection "FollowerList", with documents named with the User's UID. Within these documents is a "uid" field, with the value of the User's UID.
Any help would be massively appreciated.
This should do the Job:
func deleteAccountButtonIsTapped(_ sender: Any) {
let db = Firestore.firestore()
let userID = Auth.auth().currentUser?.uid
let username = usernameTextField.placeholder
Auth.auth().currentUser?.delete(completion: { (error) in
if error != nil {
print("ERROR MAIN SETTINGS 136")
} else {
db.collection("FollowerList").whereField("uid", isEqualTo: userID!).getDocuments { (snapshot, error) in
if let snapshot = snapshot?.documents {
for doc in snapshot {
//Do delete
db.collection("FollowerList").document(doc.documentID).updateData([
"fieldToDelete": FieldValue.delete(),
]) { err in
if let err = err {
print("Error updating document: \(err)")
} else {
print("Document successfully updated")
}
}
}
}
}
}
}
)}
One would think it could work like this:
But it doesn't as a value of type 'QueryDocumentSnapshot' has no member 'updateData'.
func deleteAccountButtonIsTapped(_ sender: Any) {
let db = Firestore.firestore()
let userID = Auth.auth().currentUser?.uid
let username = usernameTextField.placeholder
Auth.auth().currentUser?.delete(completion: { (error) in
if error != nil {
print("ERROR MAIN SETTINGS 136")
} else {
db.collection("FollowerList").whereField("uid", isEqualTo: userID!).getDocuments { (snapshot, error) in
if let snapshot = snapshot?.documents {
for doc in snapshot {
// How one would think it works but it doesnt
doc.updateData([
"capital": FieldValue.delete(),
]) { err in
if let err = err {
print("Error updating document: \(err)")
} else {
print("Document successfully updated")
}
}
}
}
}
}
}
)}
See this page for further information:
https://firebase.google.com/docs/firestore/manage-data/delete-data#swift

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)
})
}
})
}
}

DispatchGroup will only exit when method is called twice

I'm trying to sign up users with Firebase auth. When a user signs up, I'd like them to be added to my Users collection in Firestore as well as the Users authorization section.
The createUser(withEmail: ...) method works every time. However, my db.collection("users").document(user.id).setData([..] method will only be called if I press the sign up button twice, and at that point the createUser(withEmail ...) method gets called again. Here's the relevant code
SignupViewController.swift
#IBAction func signupButtonTapped(_ sender: UIButton) {
// user: User() defined here
usersHelper.signup(user: user, password: password) { result in
// This closure is only executed on the second press
guard let user = result as? Firebase.User else {
let error = result as? Error
self.handleSignupError(error!)
return
}
self.performSegue(withIdentifier: "ShowGroupsFromSignupSegue", sender: self)
}
}
UsersHelper.Swift
func signup(user: User, password: String, completion: #escaping (_ result: Any?) -> Void) {
let userDispatchGroup = DispatchGroup()
var signupError: Error? = nil
var dbError: Error? = nil
var firebaseUser: Firebase.User? = nil
userDispatchGroup.enter()
usersDataModel.signupUser(user: user, password: password) { result in
// Completion handler
if result as? Error != nil {
signupError = result as? Error
} else {
// Got the user
firebaseUser = result as? Firebase.User
}
userDispatchGroup.leave()
}
userDispatchGroup.enter()
usersDataModel.create(user: user) { err in
// This will only execute if signUp is called twice
if let result = err as? Error {
print("Error msg: \(result.localizedDescription)")
dbError = result
}
print("!Created db user")
userDispatchGroup.leave()
}
userDispatchGroup.notify(queue: .main) {
print("!dispatch group completed successfully")
if (signupError == nil && dbError == nil) {
completion(firebaseUser)
} else {
signupError != nil ? completion(signupError) : completion(dbError)
}
}
}
UsersDataModel.swift
func signupUser(user: User, password: String, _ completion: #escaping (_ err: Any? ) -> Void) {
// Create user in Auth & create DB entry
Auth.auth().createUser(withEmail: user.email, password: password) { (authResult, err) in
if let err = err {
print("Error creating user \(err)")
completion(err)
} else {
print("User signed up successfully")
completion(authResult) // completion called with User
}
}
}
func create(user: User, _ completion: #escaping (_ result: Any?) -> Void) {
// userData dictionary created here
db.collection("users").document(user.ID).setData(userData) { err in
if let err = err {
print("There was an error creating the user \(err)")
completion(err)
} else {
print("!User created in db successfully!")
completion(nil)
}
}
}
Any help is greatly appreciated! Thank you all in advance
I've resolved the error. I ended up nesting the second network call in order to:
Get the uid from the firestore who was authenticated
Not break firestore rules about writing to the database w/o an authorized uid
My UsersHelper.swift file now looks like
func signup(user: User, password: String, completion: #escaping (_ result: Any?) -> Void) {
let userDispatchGroup = DispatchGroup()
var signupError: Error? = nil
var dbError: Error? = nil
var firebaseUser: Firebase.User? = nil
userDispatchGroup.enter()
usersDataModel.signupUser(user: user, password: password) { result in
// Completion handler
if result as? Error != nil {
// there was an error?
print("Error: \(result)")
signupError = result as? Error
} else {
// Got the user
firebaseUser = result as? Firebase.User
// Create user entry in DB
user.ID = firebaseUser!.uid
self.usersDataModel.create(user: user) { err in
// Completion handler
if let err = err as? Error {
dbError = err
}
userDispatchGroup.leave()
print("Done")
}
}
}
userDispatchGroup.notify(queue: .main) {
print("!dispatch group completed successfully")
if (signupError == nil && dbError == nil) {
completion(firebaseUser)
} else {
signupError != nil ? completion(signupError) : completion(dbError)
}
}
}

user info don't load after login using the new firebase. iOS

I'm new in programming and still learning in swift language.
I hope you could help me with this issue.
when I try to signup the Userinfo shows in database but when I try to login the user info disappears why is that so? can't figure it out.
Here is how i signup
#objc func handleSignUp() {
guard let username = usernameField.text else { return }
guard let email = emailField.text else { return }
guard let pass = passwordField.text else { return }
guard let image = profileImageView.image else { return }
setContinueButton(enabled: false)
continueButton.setTitle("", for: .normal)
activityView.startAnimating()
Auth.auth().createUser(withEmail: email, password: pass) { user, error in
if error == nil && user != nil {
print("User created!")
// 1. Upload the profile image to Firebase Storage
self.uploadProfileImage(image) { url in
if url != nil {
let changeRequest = Auth.auth().currentUser?.createProfileChangeRequest()
changeRequest?.displayName = username
changeRequest?.photoURL = url
changeRequest?.commitChanges { error in
if error == nil {
print("User display name changed!")
self.saveProfile(username: username, profileImageURL: url!) { success in
if success {
self.dismiss(animated: true, completion: nil)
} else {
self.resetForm()
}
}
} else {
print("Error: \(error!.localizedDescription)")
self.resetForm()
}
}
} else {
self.resetForm()
}
}
} else {
self.resetForm()
}
}
}
And here is how I log in
#objc func handleSignIn() {
guard let email = emailField.text else { return }
guard let pass = passwordField.text else { return }
setContinueButton(enabled: false)
continueButton.setTitle("", for: .normal)
activityView.startAnimating()
Auth.auth().signIn(withEmail: email, password: pass) { user, error in
if error == nil && user != nil {
self.dismiss(animated: false, completion: nil)
_ = Auth.auth().currentUser
} else {
print("Error logging in: \(error!.localizedDescription)")
self.resetForm()
}
}
I don't know what seems to be the problem
I'm not familiar with firebase but according to their documentation this is "da way" :
Auth.auth().signIn(withEmail: email, password: password) { (user, error) in
if let error = error {
print("ERROR [ signIn ] => \(error)")
// self.resetForm()
} else {
if let user = user {
let uid = user.uid
let email = user.email
print("Authorised User => \(uid) ... \(email)")
}
}
}
If after trying the above code and you see this output ERROR [ signIn ] => whatever error...", then it's your signIn that failed. If you see this output Authorised User => SomeUID ... SomeEMAIL, then you signed in successfully and your user's data is still in the database.
I hope this helps you find "da way"
If all else fails:
You can change browsers and check your database again ? Try a couple of browsers. This worked for some people
Good luck!

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)
}
}

Resources