#IBAction func signup(_ sender: Any) {
print("began signup process")
guard let fullname = fullnameField.text, fullname != "" else {
print("FULLNAME field is empty")
return
}
guard let username = usernameField.text, username != "" else {
print("USERNAME field is empty")
return
}
guard let email = emailField.text, email != "" else {
print("EMAIL field is empty")
return
}
guard let password = passwordField.text, password != "" else {
print("PASSWORD field is empty")
return
}
print("all fields good")
mainActivityIndicator.startAnimating()
self.checkUsernameAvailability(username: username, completion: {
result in
print("starting check")
...
})
print("finished the function")
}
The issue is basically that nothing inside of the checkUsernameAvailability function will call.
The console looks like this:
began signup process
all fields good
finished the function
it does not print 'starting check' or run any code at all inside of the function.
This is probably a rookie error and I am sorry if it is a stupid question.
Future thanks.
P.S I checked the entire console and there is no error relating to this.
EDIT: Here is the code inside the function
func checkUsernameAvailability(username: String, completion: #escaping (Bool) -> Void) {
_ = Database.database().reference().child("usernames").observe(.childAdded, with: {
snapshot in
print("checking username availability")
print(snapshot)
completion(true)
let dict = snapshot.value as? [String: AnyObject]
for handled in (dict?.values)! {
print("stumbled")
print(handled)
if username.lowercased() == handled.lowercased {
completion(false)
}
}
})
}
And...
self.checkUsernameAvailability(username: username, completion: {
result in
print("starting check")
if result == false {
print("username is taken")
self.mainActivityIndicator.stopAnimating()
return
} else {
Auth.auth().createUser(withEmail: email, password: password, completion: {
(user, error) in
if error != nil {
print(error ?? "ERROR OCCURED")
self.mainActivityIndicator.stopAnimating()
return
}
let ref = Database.database().reference()
ref.child("~/users/\(user?.uid ?? "0")/username").setValue(username.lowercased())
ref.child("~/users/\(user?.uid ?? "0")/fullname").setValue(fullname)
ref.child("usernames/\(user?.uid ?? "0")").setValue(username.lowercased())
self.mainActivityIndicator.stopAnimating()
})
}
})
The Database.database().reference().child("usernames").observe call simply never calls the with block. I would assume that block is called when the event .childAdded is observed, but I see no code that would add a child. To me this looks like you are setting up an asynchronous observer, so this code will not run when you make the call, it will run when the monitored event takes place.
Related
I am working on a project using firebase database and I save the users with their displayName instead of uid. I check if the displayName of the user who is trying to signup is on the list of displayNames on firebase and if not I allow user to signup with that displayName.
here is my signUp function;
func signUp(email: String, password: String) {
//gets the user data in the firebase
Database.database().reference().child("users").observe(.value) { [self] (snapshot) in
if snapshot.hasChild(self.userNameTxtField.text!){ //checking the displayName
self.present(alertFunction(message: "Seçmiş olduğunuz kullanıcı adı daha önceden alınmış."), animated: true)
} else {
Auth.auth().createUser(withEmail: email, password: password) { (authResult, error) in
if let error = error as NSError? {...} else {
self.signIn(email: email, password: password)
}
}
}
}
}
and here is my signIn function;
func signIn(email: String, password: String) {
Auth.auth().signIn(withEmail: email, password: password) { (authResult, error) in
if let error = error as NSError? {...} else {
self.activityIndicator.stopAnimating()
self.view.isUserInteractionEnabled = true
//create database reference
self.ref = Database.database().reference()
//create new child and write a value on the existance child
if self.userNameTxtField.text == ""{
print("User Name can not be nill!")
self.present(self.alertFunction(message: "Kullanıcı adı boş bırakılamaz"), animated: true)
} else {
UserDefaults.standard.setValue(true, forKey: "userSignedIn")
if let currentUser = Auth.auth().currentUser?.createProfileChangeRequest() {
currentUser.displayName = self.userNameTxtField.text!
currentUser.commitChanges(completion: { error in
if let error = error {
print(error)
} else {
print("DisplayName changed")
self.ref.child("users").child(currentUser.displayName!).setValue(["nickname": self.userNameTxtField.text ?? ""])
self.ref.child("users/\(currentUser.displayName!)/step_count").setValue(Int(0))
self.ref.child("users/\(currentUser.displayName!)/total_point").setValue(Int(0))
self.ref.child("users/\(currentUser.displayName!)/onesignal_player_id").setValue("")
self.performSegue(withIdentifier: "showMainFromRegister", sender: self)
}
})
}
}
}
}
}
and finally here is my actionFunction;
func alertFunction(message: String) -> UIAlertController {
let alert = UIAlertController(title: "Uyarı!", message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Tamam", style: .cancel, handler: nil))
self.activityIndicator.stopAnimating()
self.view.isUserInteractionEnabled = true
return alert
}
I call the signUp function after a button clicked and signUp func is calling the signIn function and if everything goes fine signIn function calls the performSegue function but after the performSegue
snapshot.hasChild(self.userNameTxtField.text!)
is triggered several time and the alert;
self.present(alertFunction(message: "Seçmiş olduğunuz kullanıcı adı daha önceden alınmış."), animated: true)
is displaying for no reason.
How can I solve the problem?
Replace
// listens for any change
.observe(.value) { [self] (snapshot) in
with
// listens for a change only once
.observeSingleEvent(of: .value, with: { [weak self] snapshot in
I am trying to query my Firestore database to see if a desired username is taken. The query works, however I need to return a value if it is empty or not to see if the username already exists. I am trying to use a completion handler but it doesn't seem to work:
func checkUserTaken(cleanUsername: String ,completion:#escaping(String) -> (Void)){
let db = Firestore.firestore()
var userTaken: String = ""
let docRef = db.collection("users").whereField("username", isEqualTo: cleanUsername)
docRef.getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
if(querySnapshot!.documents.isEmpty){
print("user is available")
userTaken = "user is available"
}
else{
print("user is taken")
userTaken = "user is taken"
}
}
completion(userTaken)
}
This task is a good way to learn some important and helpful things about Swift, such as naming conventions, deciding what to return from functions (you chose a string, I opted for a boolean), accounting for errors, etc.
func checkUsername(_ username: String, completion: #escaping (_ taken: Bool?) -> Void) {
Firestore.firestore().collection("users").whereField("username", isEqualTo: username).getDocuments() { (snapshot, err) in
if let snapshot = snapshot {
if snapshot.documents.isEmpty {
completion(false)
} else {
completion(true)
}
} else {
if let err = err {
print(err)
}
completion(nil)
}
}
}
Usage
checkUsername("drake") { (taken) in
guard let taken = taken else {
// handle error, maybe retry?
return
}
if taken {
// prompt user username is taken
} else {
// username not taken, proceed
}
}
In the signature of the function, I labeled the boolean in the completion closure (taken), which the Swift compiler does not require you do but I think can be very helpful.
By the way, this function can return anything, even a Result object which is a neat way to return an object or an Error in a single object. But I think returning a boolean is straightforward enough here. I made the boolean an optional so the function can return three possible states (true, false, or nil) to give you a way to handle errors.
String return
func checkUsername(_ username: String, completion: #escaping (_ name: String?) -> Void) {
Firestore.firestore().collection("users").whereField("username", isEqualTo: username).getDocuments() { (snapshot, err) in
if let snapshot = snapshot {
if snapshot.documents.isEmpty {
completion(username) // return the username if it's available
} else {
completion("") // return an empty string if taken
}
} else {
if let err = err {
print(err)
}
completion(nil) // return nil if error
}
}
}
checkUsername("drake") { (name) in
guard let name = name else {
// handle error
return
}
if name.isEmpty {
// username taken
} else {
print(name) // username not taken
}
}
I'm using AWS Cognito for my app Sign In and Sign up. In my app first the user register with email and phone number. After that, I'm redirecting to Verification Screen(Here OTP is sending by Cognito) After Verifying the OTP user will create some stores and then enter into the Dashboard. In this flow, I want to get the User details Attribute from Cognito in Verification code success. I've implemented the getDetails() method to get the userAttributes in Verification code success but it is not calling. I need the userAttributes when the time of store creation. Any help appreciated.
Here is my code:
#IBAction func submitButtonAction(_ sender: GradientButton) {
let code = firstChar+secondChar+thirdChar+fourthChar+fifthChar+sixthChar
guard code.count == 6 else{
self.showAlert(message: ErrorMessages.kEnterValidOTP)
return
}
let currentUser = self.userPool?.getUser("xxxx#gmail.com")
currentUser?.confirmSignUp(code, forceAliasCreation: true).continueWith(block: { [weak self] (task) -> Any? in
guard let strongSelf = self else { return nil }
DispatchQueue.main.async {
if let error = task.error as NSError? {
if let message = error.userInfo["message"] as? String{
self?.showAlert(message: message, onOkAction: {
strongSelf.clearTextFieldData()
})
}else{
self?.showAlert(message: error.localizedDescription, onOkAction: {
strongSelf.clearTextFieldData()
})
}
}else{
print(task.result)
strongSelf.clearTextFieldData()
print(AWSUserDetails.shared.userPool.currentUser()?.username)
let user = AWSUserDetails.shared.userPool.currentUser()
//I've tried the above `user` and `currentUser`. But not working.
user?.getDetails().continueOnSuccessWith(block: { (task) -> Any? in
DispatchQueue.main.async {
if task.error == nil{
print(task.error)
}else{
print(task.result)
}
}
})
// strongSelf.performSegue(withIdentifier: SegueIdentifiers.createStoreSegue, sender: self)
}
}
return nil
})
}
I have three text field in my registration form. The e-mail id and password of the user are used by the Firebase SignUp method to create a new user. But I also want to save the user's name according to what they input.
My current code is;
#IBAction func registerPressed(_ sender: Any) {
SVProgressHUD.show(withStatus: "Setting you up")
dismissUIElements(value: false)
let currentUserName = userName.text
if currentUserName?.isEmpty == false {
FIRAuth.auth()?.createUser(withEmail: emailTextField.text!, password: passwordTextField.text!, completion: { (user, error) in
if error != nil {
print(error!)
SVProgressHUD.dismiss()
self.dismissUIElements(value: true)
} else {
print("Registration Successful!")
SVProgressHUD.dismiss()
self.dismissUIElements(value: true)
self.performSegue(withIdentifier: "goToSelectionFromRegister", sender: self)
}
})
}
else {
SVProgressHUD.dismiss()
SVProgressHUD.showError(withStatus: "Please enter your name!")
SVProgressHUD.dismiss(withDelay: 1)
self.dismissUIElements(value: true)
}
}
You need a function that will register the user and then create the child for that user in Firebase.
let databaseRef=//your path to users.
func registerUser(userUsername userName:String, userEmail email:String, userPassword password: String, userCreationComplete: #escaping (_ status: Bool, _ error: Error?) -> ()) {
Auth.auth().createUser(withEmail: email, password: password) { (user, error) in
guard let user = user else {
userCreationComplete(false, error)
return
}
let userData = ["userName": userName.text] as [String : Any]
ref.child(user.uid).updateChildValues(userData)
userCreationComplete(true, nil)
}
}
Then you call the function from within registerPressed() and you pass it the textfield values but make sure that none of them is empty.
You will create a new data table that stores that info. It won't be done in the create user function.
// create a reference to the DB
ref = Database.database().reference(fromURL: "https://your-project-name.firebaseio.com/")
//variables to store data
var myID : String?
var myName : String?
var myNumber : String?
var myEmail : String?
// creating the save user function
func saveUser(_ completion: #escaping(_ error: Error?) -> Void) {
if PageDataSource.sharedInstance.crudIsAvailable == true {
let usersRef = ref.child("users")
let myUserRef = usersRef.child(id)
myUserRef.updateChildValues(["User ID": id,
"Name": myName,
"Email": myEmail,
"Phone": .myNumber], withCompletionBlock: { (error, ref) in
if error != nil {
completion(error!)
} else {
completion(nil)
}
})
} else {
completion(NSError(domain: "Unavailable", code: 0, userInfo: nil))
}
}
// call the method like this to perform the save
func storeUser(completion: #escaping(_ completed: Bool, _ error: NSError?)-> Void) {
if let user = Auth.auth().currentUser {
myID = user.uid
myName = user.displayName
myEmail = user.email
// etc.,
completion(true,nil)
} else {
completion(false,NSError(domain: "No Current User", code: 1, userInfo: nil))
}
}
Recently my facebook login hasn't been working. It doesn't save into my database but in Firebase authentication it shows someone signed in with facebook but the email is blank. It doesn't save any data to my database which it should. The email is returning nil. My google sign in works. I've checked all the connections and they all seem to be fine. (I could have missed something) anyone have any suggestions on what connections I should check? Not sure what to do...
More Info
I printed my graph request... not sure if this helps to debug
let loginManager: FBSDKLoginManager = FBSDKLoginManager()
loginManager.logIn(withReadPermissions: self.facebookPermissions, from: self, handler: { (result, error) in
if error != nil {
loginManager.logOut()
let message: String = "An error has occured. \(String(describing: error))"
let alertView = UIAlertController(title: "Alert", message: message, preferredStyle: UIAlertControllerStyle.alert)
alertView.addAction(UIAlertAction(title: "Ok ", style: UIAlertActionStyle.default, handler: nil))
self.present(alertView, animated: true, completion: nil)
} else if (result?.isCancelled)! {
// user cancelled login
loginManager.logOut()
} else {
let accessToken = FBSDKAccessToken.current()
guard let accessTokenString = accessToken?.tokenString else { return }
let credential = FacebookAuthProvider.credential(withAccessToken: accessTokenString)
Auth.auth().signIn(with: credential) { (user, error) in
if (error != nil) {
// handle error
print(error ?? "Error")
} else {
let ref = Database.database().reference()
// guard for user id
guard let uid = user?.uid else {
return
}
let usersReference = ref.child("user_profiles").child(uid)
let graphRequest : FBSDKGraphRequest = FBSDKGraphRequest(graphPath: "me", parameters: ["fields": "id, name, email"])
graphRequest.start(completionHandler: { (connection, result, error) -> Void in
if error != nil {
// Process error
print("Error: \(String(describing: error))")
} else {
guard let data: [String:AnyObject] = result as? [String:AnyObject] else {
print("Can't pull data from JSON")
return
}
guard let userName: String = data["name"] as? String else {
print("Can't pull username from JSON")
return
}
guard let userID: String = data["id"] as? String else {
print("Can't pull ID from JSON")
return
}
let imgURLString = "http://graph.facebook.com/\(userID)/picture?type=large" as String
guard let userEmail: String = data["email"] as? String else {
print("Can't pull email from JSON")
print("Error: \(String(describing: error))")
return
}
// initial # posts = 0
let values = ["name": userName, "email": userEmail, "facebookID": userID, "profPicString": imgURLString] as [String : Any]
// update database with new user
usersReference.updateChildValues(values, withCompletionBlock: { (err, ref) in
// error in database save
if err != nil {
print(err ?? "Error saving user to database")
return
}
})
}
})
self.dismiss(animated: false, completion: nil)
// Present the main view
if let viewController = self.storyboard?.instantiateViewController(withIdentifier: "Customer Profile") {
UIApplication.shared.keyWindow?.rootViewController = viewController
self.dismiss(animated: true, completion: nil)
}
}
}
}
})
Using Swift 5 I found the email inside providerData which is an array of FIRUserInfo:
if AccessToken.current != nil {
let credential = FacebookAuthProvider.credential(withAccessToken: AccessToken.current!.tokenString)
Auth.auth().signIn(with: credential) { (res, err) in
if err != nil || res == nil {
//...
return
}
guard let providerData = res?.user.providerData else {
//...
return
}
for firUserInfo in providerData {
print(firUserInfo.providerID)
print(firUserInfo.email ?? "Email not found")
}
}
}