I'm trying to register a user and verify their email. I've done it and verified it on the email but on Auth.auth().currentUser.isEmailVerified
if (Auth.auth().currentUser?.isEmailVerified)! {
// present another vc
} else {
print("not verified yet") //Always prints that
}
here's my sendVerificationEmail function:
if let user = Auth.auth().currentUser {
user.sendEmailVerification(completion: { (error) in
if let error = error {
debugPrint(error)
return
}
print("Sent VerificationMail")
})
} else {
print("no user logged in")
}
here i register the user:
func registrateUser(email: String, password: String, completion: #escaping (Bool) -> Void) {
Auth.auth().createUser(withEmail: email, password: password) { (result, error) in
if let error = error {
debugPrint(error)
completion(false)
return
}
result?.user.sendEmailVerification(completion: { (error) in
if let error = error {
debugPrint(error)
completion(false)
return
}
completion(true)
})
}
}
You need to reload user profile before checking if email is verified.
Use following code:
Auth.auth().currentUser?.reload(completion: { (error) in
guard error == nil else {
// handle error
print(error!.localizedDescription)
return
}
// your code below
if (Auth.auth().currentUser?.isEmailVerified)! {
// present another vc
} else {
print("not verified yet") //Always prints that
}
})
Related
I would like to have global func signIn that I can use inside my app but my problem is that I need to call some functions after the user is created. I thought I could use a completion handler for that but I tried it like this which gives me an error:
static func signIn(credentials: Any?, username: String, finished: () -> Void){
Auth.auth().signIn(with: credentials as! AuthCredential, completion: { (user, error) in
if error != nil {
Utilities.showErrorPopUp(labelContent: "Fehler bei Kontoerstellung", description: error!.localizedDescription)
} else {
//user was created successfully; store name, username and UID
let db = Firestore.firestore()
let userID = user!.user.uid
db.collection("users").document(userID).setData(["username": username, "uid": user!.user.uid]) { (error) in
if error != nil {
Utilities.showErrorPopUp(labelContent: "Fehler", description: error!.localizedDescription)
}
}
// generate empty "Main Wishlist"
db.collection("users").document(userID).collection("wishlists").document("Main Wishlist").setData(["name": "Main Wishlist", "listIDX": 1]) { (error) in
if error != nil {
Utilities.showErrorPopUp(labelContent: "Fehler", description: error!.localizedDescription)
}
}
// set user status to logged-in
UserDefaults.standard.setIsLoggedIn(value: true)
UserDefaults.standard.synchronize()
finished()
}
})
}
Error:
Escaping closure captures non-escaping parameter 'finished'
Before the change my function looked like this:
Auth.auth().signIn(with: credentials, completion: { (user, error) in
if error != nil {
Utilities.showErrorPopUp(labelContent: "Fehler bei Kontoerstellung", description: error!.localizedDescription)
} else {
//user was created successfully; store name, username and UID
let db = Firestore.firestore()
let userID = user!.user.uid
db.collection("users").document(userID).setData(["username": username, "uid": user!.user.uid]) { (error) in
if error != nil {
Utilities.showErrorPopUp(labelContent: "Fehler", description: error!.localizedDescription)
}
}
// generate empty "Main Wishlist"
db.collection("users").document(userID).collection("wishlists").document("Main Wishlist").setData(["name": "Main Wishlist", "listIDX": 1]) { (error) in
if error != nil {
Utilities.showErrorPopUp(labelContent: "Fehler", description: error!.localizedDescription)
}
}
// set user status to logged-in
UserDefaults.standard.setIsLoggedIn(value: true)
UserDefaults.standard.synchronize()
// stop animation
self.logoAnimation.stop()
//transition to home
self.transitionToHome()
}
})
}
As you can see in this example I am calling self.logoAnimation.stop() and self.transitionToHome().
How can I outclass the method but still call methods when the user is signed up?
If anything is unclear just let me know :)
EDIT: I added the batch write.
static func signIn(credentials: Any?, username: String, finished: #escaping (_ done: Bool) -> Void) {
guard let credentials = credentials as? AuthCredential else {
finished(false)
return
}
Auth.auth().signIn(with: credentials, completion: { (result, error) in
if let userId = result?.user.uid { // successfully signed in
let batch = Firestore.firestore().batch()
batch.setData(["username": username, "uid": userId], forDocument: Firestore.firestore().collection("users").document(userId), merge: true)
batch.setData(["name": "Main Wishlist", "listIDX": 1], forDocument: Firestore.firestore().collection("users").document(userId).collection("wishlists").document("Main Wishlist"), merge: true)
batch.commit { (error) in
if let error = error {
print(error)
// maybe sign user out and on completion call finished(false)
// whatever you do, you must call finished(false) at some point
} else {
UserDefaults.standard.setIsLoggedIn(value: true)
UserDefaults.standard.synchronize()
finished(true) // sign-in process complete
}
}
} else { // could not sign in
if let error = error {
print(error)
}
finished(false)
}
})
}
The call to this method would look like this:
signIn(credentials: credentials, username: someString, finished: { (done) in
if done { // success
...
} else { // failure
...
}
}
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)
})
}
})
}
}
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)
}
}
}
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!
InFirebase 2.5.1, I used to do this, and it was working:
#IBAction func facebookLoginDidTouch(sender: AnyObject) {
let facebookLogin = FBSDKLoginManager()
facebookLogin.logInWithReadPermissions(["public_profile", "email"], fromViewController: self, handler: {
(facebookResult, facebookError) -> Void in
if facebookError != nil {
print("Facebook login failed. Error \(facebookError)")
} else if facebookResult.isCancelled {
print("Facebook login was cancelled.")
} else {
let accessToken = FBSDKAccessToken.currentAccessToken().tokenString
myRootRef.authWithOAuthProvider("facebook", token: accessToken, withCompletionBlock: { error, authData in
if error != nil {
print("Login failed. \(error)")
} else {
print("Logged in!")
let newUser = [
"provider": user.provider,
"imageUrl": user!.providerData["profileImageURL"] as String, // etc
]
}
})
}
})
}
Now I am trying to achieve this in Firebase 3.x. However, I got confused on where I should place permissions and stuff
This is what I tried so far
#IBAction func facebookLoginDidTouch(sender: AnyObject) {
let facebookLogin = FBSDKLoginManager()
let credential = FIRFacebookAuthProvider.credentialWithAccessToken(FBSDKAccessToken.currentAccessToken().tokenString)
FIRAuth.auth()?.signInWithCredential(credential) { (user, error) in
if error != nil {
print("Login failed. \(error)")
} else {
print("Logged in!")
let newUser = [
"provider": user.provider,
"imageUrl": user!.providerData["profileImageURL"] as String
]
}
})
}
Update:
facebookLogin.logInWithReadPermissions(["public_profile", "email"], fromViewController: self, handler: {
(facebookResult, facebookError) -> Void in
if facebookError != nil {
print("Facebook login failed. Error \(facebookError)")
} else if facebookResult.isCancelled {
print("Facebook login was cancelled.")
} else {
// your firebase authentication stuff..
let credential = FIRFacebookAuthProvider.credentialWithAccessToken(FBSDKAccessToken.currentAccessToken().tokenString)
FIRAuth.auth()?.signInWithCredential(credential) { (user, error) in
if error != nil {
print("Login failed. \(error)")
} else {
print("Logged in!")
let userID = Helpers.extractUID(user!)
let rootRef = FIRDatabase.database().reference()
let userRef = rootRef.child("users").child(userID)
userRef.observeEventType(.Value, withBlock: { snapshot in
if snapshot.value is NSNull {
let newUser = [
"providerId": user?.providerID,
"displayName": user?.displayName,
]
userRef.setValue((newUser as! AnyObject))
// perform segue
}
})
}
})
After you successfully login with facebook and get user data back ... you have to add firebase authentication stuff like
#IBAction func facebookLoginDidTouch(sender: AnyObject) {
let facebookLogin = FBSDKLoginManager()
facebookLogin.logInWithReadPermissions(["public_profile", "email"], fromViewController: self, handler: {
(facebookResult, facebookError) -> Void in
if facebookError != nil {
print("Facebook login failed. Error \(facebookError)")
} else if facebookResult.isCancelled {
print("Facebook login was cancelled.")
} else {
// your firebase authentication stuff..
let credential = FIRFacebookAuthProvider.credentialWithAccessToken(FBSDKAccessToken.currentAccessToken().tokenString)
FIRAuth.auth()?.signInWithCredential(credential) { (user, error) in
if error != nil {
print("Login failed. \(error)")
} else {
print("Logged in!")
}
})
}
})
}