How to add a username to Firebase database upon registration? - ios

Upon logging in I want my app to make a new field in the Firebase database with the child named after the username.
if let email = emailField.text, let pass = passwordField.text {
// Check if it's sign in or register
if isLogin {
// Sign in the user with Firebase
Auth.auth().signIn(withEmail: email, password: pass, completion: { (user, error) in
// Check that user isn't nil
if let u = user {
// User is found, go to home screen
self.ref?.child(email).childByAutoId().setValue("1")
self.performSegue(withIdentifier: "goHome", sender: self)
print("yes")
}
When I try to do this, it gives me an error called SIGABART which I believe is associated with not having segues connected properly.
Yet if I delete this line:
self.ref?.child(email).childByAutoId().setValue("1")
or change the email field to a random string like "test", it works fine and appears in Firebase.

If I remember correctly, you can't use symbol # in nodes names. It's first problem. You can do it another, I think better, way:
You need to create user to ref like:
/users/uid from created FIRUser.
You can do it with next steps:
For example, your registration page will have 3 UITextFields: userEmail, userLogin and userPassword.
// *1* Create user
FIRAuth.auth()!.createUser(withEmail: userEmail.text!,
password: userPassword.text!)
{ user, error in
if error == nil {
// *2* Then log him in
FIRAuth.auth()!.signIn(withEmail: self.userEmail.text!,
password: self.userPassword.text!)
{ result in
// *3* Create new user in database, not in FIRAuth
let uid = (FIRAuth.auth()?.currentUser?.uid)!
let ref = FIRDatabase.database().reference(withPath: "someStartPart/users").child(uid)
ref.setValue(["uid": uid, "email": userEmail.text!, "login": userLogin.text!, "creationDate": String(describing: Date())])
self.performSegue(withIdentifier: "fromRegistrationToMainPage", sender: self)
}
} else {
print("\(String(describing: error?.localizedDescription))")
}
}
Like this. Hope it helps.

Firebase Auth will not allow you to store such a value. You will need to save this into Firebase Database or another service. e.g. In the Firebase Database:
userRef.setValue("username123")

You should save the username value in the database. Something like this:
FIRdatabase.database().reference().child("yourchildname").updateChild(u)
(Im on my phone, so the call might not be EXACTLY like that, but should be very close)
Cheers.

Swift 4:
//***** for realTime database
let ref = Database.database().reference(fromURL: "https://add your project URL")
let userRef = ref.child("users").child(user.user.uid)
let values = ["Email": email, "UserName": userName] //userName is the name of textField

Related

SwiftUI Edit User Account using data from Firestore

I'm very new to SwiftUI and I'm trying to create a very basic login/logout system in SwiftUI on IOS with the ability to edit the users profile. I have two main functions in the signup process, function register and function AddInfo which then actually adds the user to the db.
both in AuthenticationView.swift
func register(){
if self.email != ""{
if self.pass == self.repass{
//Creating the user
Auth.auth().createUser(withEmail: self.email, password: self.pass) { (res, err) in
if err != nil{
self.error = err!.localizedDescription
self.alert.toggle()
return
}
//Add user info to database
AddInfo(username: self.username, email: self.email)
print("success")
UserDefaults.standard.set(true, forKey: "status")
NotificationCenter.default.post(name: NSNotification.Name("status"), object: nil)
}
}
else{
self.error = "Password mismatch"
self.alert.toggle()
}
}
else{
self.error = "Please fill all the contents properly"
self.alert.toggle()
}
}
Then i have an addinfo function which adds the user to a database. The document ID of the db is the users UID and then it creates a username, email and uid automatically and i've made it create a blank entry for Firstname and Lastname.
func AddInfo(username: String, email: String) {
let db = Firestore.firestore()
let user = Auth.auth().currentUser
if let user = user {
// The user's ID, unique to the Firebase project.
// Do NOT use this value to authenticate with your backend server,
// if you have one. Use getTokenWithCompletion:completion: instead.
let uid = user.uid
//let email = user.email
//let photoURL = user.photoURL
db.collection("users").document(uid).setData([
"UID": uid,
"Username": username,
"Email": email,
"First Name": "",
"Last Name": ""])
}
This is what the DB looks like when a user registers
database look when user registers
So with all of this in mine, i'm struggling with creating a Edit user profile view. How would I go about doing this? All i need is a page that shows the users registered email, username and then they can add their first and last name (first and last name isn't included on the registration form, that's something i'd like to add after they've registered)
I've tried reading the Firestore view my profile document but i'm struggling to get my head around it

why do i get two user ids when creating a user in firebase using swift?

when i sign up a user in my ios app it generates a user id and adds that to the data base with the users name and surname and username but it is generating a user id and another random number/id and i dont know what that is for:
i dont know what the Roy... is and dont know where its coming from.
so when i try and access the users uid to access the information such as the name and surname i keep getting the following error because its using the wc7... number and not the other one:
Listener at /Users/wc7VyejKlDNfcAhFu3AkIX9Y9on1/Username failed: permission_denied
this is my code that i use to try and access the users information:
func fetchUsersData() {
guard let currentUser = Auth.auth().currentUser?.uid else { return }
print("Current user id is \(currentUser)")
Database.database().reference().child("Users").child(currentUser).child(USER_NAME).observeSingleEvent(of: .value) { (snapshot) in
guard let username = snapshot.value as? String else {return}
self.navigationItem.title = username
}
}
how do i fix this?
That code is for the Firebase Real Time Database and the screen shot is for Cloud Firestore. They are totally different and unrelated.
If you want to read the data shown in your screenshot you need to use the Cloud Firestore documentation.
The documentID 'Roy...' happens when you don't assign a document an ID... it will generate one automatically.
Also, change your Firestore structure to use the users uid as the documentID. So it would look like this
users //the collection
uid_0 //the document with documentID = a users uid
first_name: "Hank"
last_name: "Jones"
user_name: "Hankster"
uid_1
first_name: "Leroy"
last_name: "Jenkins"
user_name: "Leeeerrroooyyy"
and then the code to read a specific user name based on a uid is this
func readUserName() {
let users = self.db.collection("users")
let thisUser = users.document(the users uid)
thisUser.getDocument(completion: { documentSnapshot, error in
if let error = error {
print(error.localizedDescription)
return
}
guard let snap = documentSnapshot else { return }
let docId = snap.documentID
let userName = snap.get("user_name") as? String ?? "No Name"
print(userName)
})
}
Also note that you will need to be authenticated to Firestore to read any data or adjust the Security Rules to allow anyone to read. That's not generally a good idea but when you're just getting started it's ok.

Swift iOS Firebase -If the FirebaseAuth.FIRUser Object isn't nil how is it possible for the .email on it to return nil?

I use Firebase's Email/Password Sign-In Method to create an account for a user. Using that method the user must have an email address to get authenticated into Firebase.
FirebaseAuth has a FIRUser object named User
When a user first creates an account or logs into an existing account in the callback the User object gets initialized:
Create Account:
Auth.auth().createUser(withEmail: emailTextField.text!, password: passwordTextField.text!, completion: {
(user, error) in
// user's auth credentials are now created but user can still be nil
Logging into existing account:
Auth.auth().signIn(withEmail: self.emailTextField.text!, password: self.passwordTextField.text!, completion: {
(user, error) in
// user's auth credentials are now accessed but user can still be nil
The User object has an .email Optional of type String on it that contain's the user's email address.
I use a singleton class to manage everything that happens through Firebase and in both situations above when the User objects gets initialized I pass that data through to my Firebase singleton's class properties. I know the User object can come back as nil so I run an if-let to make sure it isn't.
My question is if using the Email/Password Sign-In Method if the User object isn't nil in the callback is it possible that the .email can be nil even though it's necessary to have it to make an account or log in?
After I run the if-let statements I use guard statements to check and see if the .email isn't nil but it seems like it either can run before the the FirebaseSingleton properties gets initialized (which means it will be nil) or it might be unnecessary if the User object is guaranteed to return it with a value.
Btw in both situations wether the user is creating and account or logging in the emailTextFiled and passwordTextField will not be nil. I run whitespace, .isEmpty, and != nil checks on them.
My Code:
class FirebaseSingleton{
static let sharedInstance = FirebaseSingleton()
var dbRef: DatabaseReference? = Database.database().reference()
var storageDbRef: StorageReference? = Storage.storage().reference(forURL: "gs://blablabla.appspot.com")
var currentUser: User? = Auth.auth().currentUser
var currentUserID: String? = Auth.auth().currentUser?.uid
var currentUserEmail: String? = Auth.auth().currentUser?.email
var currentUserPhotoUrl: URL? = Auth.auth().currentUser?.photoURL
var currentUserDisplayName: String? = Auth.auth().currentUser?.displayName
}
Creating an Account:
Auth.auth().createUser(withEmail: emailTextField.text!, password: passwordTextField.text!, completion: {
(user, error) in
if error != nil { return }
if let user = user{
let sharedInstance = FirebaseSingleton.sharedInstance
sharedInstance.currentUser = user
sharedInstance.currentUserID = user.uid
sharedInstance.currentUserEmail = user.email // at this point is it possible for this to be nil?
sharedInstance.currentUserPhotoUrl = user.photoURL
sharedInstance.currentUserDisplayName = user.displayName
}else{
return
}
// is this guard statement necessary?
guard let email = user.uid else { return }
})
Logging into an existing account:
Auth.auth().signIn(withEmail: emailTextField.text!, password: passwordTextField.text!, completion: {
(user, error) in
if error != nil { return }
if let user = user{
let sharedInstance = FirebaseSingleton.sharedInstance
sharedInstance.currentUser = user
sharedInstance.currentUserID = user.uid
sharedInstance.currentUserEmail = user.email // at this point is it possible for this to be nil?
sharedInstance.currentUserPhotoUrl = user.photoURL
sharedInstance.currentUserDisplayName = user.displayName
}else{
return
}
// is this guard statements necessary?
guard let email = user.uid else { return }
})
Not sure exactly what are you asking for (since to me looks more like a swift question than a firebase one), but as long as the user isn't nil, the uid property is not an optional (String) so user.uid will never be nil. it is not the same for the email which is an String? therefore might or not be nil
After having a convo with #Benjamin Jimenez and per #kbunarjo suggestions I did some thinking and I realized that Firebase also has a Phone Sign-In Method. If it's enabled and a user chooses that as their sign-in method then the .email address would be nil because there wouldn't be an email address used to create an account.
For my situation since I'm using the Email/Password Sign-In Method then the .email should not be nil because it is the only way to create an account. But just as User can come back as nil in the completion handler (even with the correct email and password) maybe it’s possible that the email address can also come back as nil since it’s an Optional.
That being said the safest method to use would be another if-let to check to see if the user.email isn't nil and if for some strange reason that it is then use the email address that was entered into the emailTextField as an alternative. The email address entered into the emailTextField and password combo has to be correct otherwise the user won't get authenticated into Firebase and error != nil.
Auth.auth().signIn(withEmail: emailTextField.text!, password: passwordTextField.text!, completion: {
(user, error) in
// if the email address and/or password are incorrect then error != nil and the code below this wont run
if error != nil { return }
if let user = user{
let sharedInstance = FirebaseSingleton.sharedInstance
sharedInstance.currentUser = user
sharedInstance.currentUserID = user.uid
sharedInstance.currentUserPhotoUrl = user.photoURL
sharedInstance.currentUserDisplayName = user.displayName
// instead of the guard statement I use another ‘if-let’ if for some reason .email is nil
if let currentUserEmail = user.email {
sharedInstance.currentUserEmail = currentUserEmail
}else{
sharedInstance.currentUserEmail = self.emailTextField.text! // the email address entered into here has to be valid otherwise it would've never made it to this point
}
}else{
return
}
})
Use the same exact code for the callback if Creating an Account
As #rMickeyD noted the Facebook Sign-In Method won’t use the email address either. I’ve never used it so I didn’t include it. Basically if none of the other Sign-In Methods don’t use your email address then user.email will be nil. Even if though I have Anonymous Sign-In as an option the method to use Anonymous Sign-In doesn’t require an email address nor a password so it’s understood that email will be nil.
You should check the docs to see if they require the email or not for all the other Sign-In Methods but the Phone number doesn’t need it.

Facebook login using Firebase - Swift iOS

I'm implementing login with Facebook using Firebase, I have this code which searches my database after a successful facebook authentication for the email if exists in database and logs in the app if found, I want to direct the user to registration view controller if not found but its not working since this method is asynchronous. I appreciate if anyone can help. Here is my code :
func getFacebookUserInfo() {
if(FBSDKAccessToken.current() != nil){
let graphRequest = FBSDKGraphRequest(graphPath: "me", parameters: ["fields" : "id,name,gender,email,education"])
let connection = FBSDKGraphRequestConnection()
connection.add(graphRequest, completionHandler: { (connection, result, error) -> Void in
let data = result as! [String : AnyObject]
let email = data["email"] as? String
let emailRef = FIRDatabase.database().reference().child("usernameEmailLink")
emailRef.queryOrderedByValue().observe(.childAdded, with: { snapshot in
if let snapshotValue = snapshot.value as? [String: AnyObject] {
for (key, value) in snapshotValue {
if(value as? String == email){
self.stringMode = snapshotValue["mode"]! as! String
self.username = key
self.parseUserInfoFromJSON()
return
}
}
}
})
})
connection.start()
}
}
Thank you.
The registration/existence of the user in Firebase should probably be determined before the graphRequest code in the question.
Most importantly, (and this is critical), email addresses are dynamic so they should not be used to verify if a user exists. i.e. user with email address of 'leroy#gmail.com' updates his email to 'leroy.j#gmail.com'. If emails are used to verify registration, it can totally break if that email changes.
Please use Firebase uid's for that purpose as they are static and unique.
Since we only have a small snippet of code, we don't know the exact sequence being used. This answer is pseudo-code to outline a possible sequence.
We assume that by 'registered' it means that the user has gone through some kind of app registration sequence and the user has been created (and now exists/is registered) in Firebase.
In general there would be a login button and a delegate method to handle the actual login action.
The user enters their login and taps the login button
func loginButton(loginButton: FBSDKLoginButton!,
didCompleteWithResult result: FBSDKLoginManagerLoginResult!,
error: NSError?) {
Firebase can then get the credentials for that user (see Firebase doc quote below)
let credential = FIRFacebookAuthProvider.credential(withAccessToken: FBSDKAccessToken.current().tokenString)
At that point, sign in the user and check to see if they are registered (exist) in the Firebase user node.
FIRAuth.auth()?.signIn(with: credential) { (user, error) in
if let error = error { //failed due to an error
return
}
let uid = user.uid //the firebase uid
let thisUserRef = userRef.child(uid) //a reference to the user node
//check to see if the user exists in firebase (i.e. is Registered)
thisUserRef.observeSingleEvent(of: .value, with: { (snapshot) in
//if snapshot exists
//then the user is already 'registered' in the user node
// so continue the app with a registered user
//if not, then need to have the user go through a registration sequence and
// then create the user (make them registered) in the user node
doRegisterUser(user)
})
func doRegisterUser(user: FIRUser) {
//get what you need from the user to register them
// and write it to the users node. This could be from additional
// questions or from their Facebook graph, as in the code in the
// question
//for this example, we'll just write their email address
let email = user.email
let dict = ["email": email]
//create a child node in the users node with a parent of uid
// and a child of email: their email
thisUserRef.setValue(node)
//next time the user logs in via FB authentication, their user node
// will be found as they are now a 'registered' user
}
From the Firebase docs
After a user signs in for the first time, a new user account is
created and linked to the credentials—that is, the user name and
password, or auth provider information—the user signed in with. This
new account is stored as part of your Firebase project, and can be
used to identify a user across every app in your project, regardless
of how the user signs in.
As I mentioned, this is very pseudo code but offers a possible sequence for a solution.

Add Extra Parse Log In Field

I am making an app that logs you in to a certain group, Currently I have it where you log in as such:
let loginUser = PFUser()
loginUser.username = username.text!
loginUser.password = password.text!
PFUser.logInWithUsername(inBackground: loginUser.username!, password: loginUser.password!, block: { (user, error) in
if user != nil { //Continue with code
but what I'd like to do would be to add in my group login ID field so that I do not have to make an additional call to match their groupID TextField with their currently set up groupID. I don't know if you can and I couldn't find it in documentation. Something Like:?
let loginUser = PFUser()
loginUser.username = username.text!
loginUser.password = password.text!
loginUser["groupID"] = groupID.text!
PFUser.logInWithUsername(inBackground: loginUser.username!, password: loginUser.password!, login["groupID"]: login.groupID block: { (user, error) in
if user != nil { //Now Checks login AND for group id...
Now obviously I know it's not that easy but does anybody have a mildly easy work around for this? Right now afterwords i'm comparing the
user["groupID"] == groupID.text! {
// Then Login to group is a success!
// Segue to next View Controller
} else {
// Let the user know the Log In was "unsuccessful" even though they logged in but they got their groupID wrong
// Log User Out
// Clear groupID Text Field & Display alert so they can reattempt log in
}
Any help would be appreciated!
Thanks!
I suggest you to use "cloud code", you might want to create your own login method using some other language like javascript.

Resources