Im trying to update a variable inside a class "var CurrentStatus:status! " status is an enum. I have a firebase function that will update the variable. the variable get update inside the firebase function but it will not update the variable outside of the firebase function
class signUpClass:UIViewController {
// check to see if form is empty
let ihelpController = UIViewController()
var CurrentStatus:status!
func signUp(var formArray: [String:String]) -> status{
var formStatus:status = ihelpController.checkIfFormIsEmpty(formArray)
if (formStatus == status.success){
//form is ok to process
// check DOB
//TODO: create date calculation function
let DateOfBirth:Int = 18
if DateOfBirth < 18 {
//user is not 18 they can not register
alertError("oops", message: "You must be 18 to register", comfirm: "Ok")
} else {
//Proceed with registration
let firebaseController = Firebase()
var email = "asdf#afd.com"
var password = "1234"
firebaseController.refPath("users").createUser(email, password: password, withValueCompletionBlock: {error, result in
if error != nil {
print("registration Error")
self.alertError("oops", message: "That email is registered already", comfirm: "OK")
} else {
let vc =
print("user can register")
firebaseController.firebaseRefUrl().authUser(email, password: password, withCompletionBlock:{
error, authdata in
if error != nil {
print("login Error")
}else{
let userId = firebaseController.firebaseRefUrl().authData.uid
formArray["userId"] = userId
firebaseController.refPath("users/\(userId)").updateChildValues(formArray)
print("user is register and can proceed to dashBoard")
//Proceed to dashboard
self.CurrentStatus = status.success
}
})
}
})
}
}
return CurrentStatus
}
Agreed with Jay's comment. You cannot return the status like that because Firebases works asynchronously... what I would do, is add a closure parameter that executions on completion like so:
class signUpClass:UIViewController {
// check to see if form is empty
let ihelpController = UIViewController()
var CurrentStatus:status!
func signUp(var formArray: [String:String], complete:(CurrentStatus)->()){
var formStatus:status = ihelpController.checkIfFormIsEmpty(formArray)
if (formStatus == status.success){
//form is ok to process
// check DOB
//TODO: create date calculation function
let DateOfBirth:Int = 18
if DateOfBirth < 18 {
//user is not 18 they can not register
alertError("oops", message: "You must be 18 to register", comfirm: "Ok")
} else {
//Proceed with registration
let firebaseController = Firebase()
var email = "asdf#afd.com"
var password = "1234"
firebaseController.refPath("users").createUser(email, password: password, withValueCompletionBlock: {error, result in
if error != nil {
print("registration Error")
self.alertError("oops", message: "That email is registered already", comfirm: "OK")
} else {
let vc =
print("user can register")
firebaseController.firebaseRefUrl().authUser(email, password: password, withCompletionBlock:{
error, authdata in
if error != nil {
print("login Error")
}else{
let userId = firebaseController.firebaseRefUrl().authData.uid
formArray["userId"] = userId
firebaseController.refPath("users/\(userId)").updateChildValues(formArray)
print("user is register and can proceed to dashBoard")
//Send status to callback to handle
complete(status.success)
}
})
}
})
}
}
}
Related
I want to retrieve the current logged in user Information (name and email) that was stored in the firestore in the registration function, the email and name should be displayed in textfield.
I can retrieve the email successfully because I’m using the Auth.auth().currentUser and not interacting with the firesotre while the name is not working for me.
what I’m suspecting is that the path I’m using for reaching the name field in firesotre is incorrect.
var id = ""
var email = ""
override func viewDidLoad() {
super.viewDidLoad()
userLoggedIn()
self.txtEmail.text = email
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
getName { (name) in
if let name = name {
self.txtUserName.text = name
print("great success")
}
}
}
func getName(completion: #escaping (_ name: String?) -> Void) {
guard let uid = Auth.auth().currentUser?.uid else { // safely unwrap the uid; avoid force unwrapping with !
completion(nil) // user is not logged in; return nil
return
}
print (uid)
Firestore.firestore().collection("users").document(uid).getDocument { (docSnapshot, error) in
if let doc = docSnapshot {
if let name = doc.get("name") as? String {
completion(name) // success; return name
} else {
print("error getting field")
completion(nil) // error getting field; return nil
}
} else {
if let error = error {
print(error)
}
completion(nil) // error getting document; return nil
}
}
}
func userLoggedIn() {
if Auth.auth().currentUser != nil {
id = Auth.auth().currentUser!.uid
//email = Auth.auth().currentUser!.email
} else {
print("user is not logged in")
//User Not logged in
}
if Auth.auth().currentUser != nil {
email = Auth.auth().currentUser!.email!
} else {
print("user is not logged in")
//User Not logged in
}
}
When I run this code the email is displayed and for the name "error getting field" gets printed so what I think is that the name of the document for user is not the same as the uid therefore the path I’m using is incorrect, the document name must be autogenerated.
So is the solution for me to change the code of the registration function?
can the user document be given a name (the userID) when I create the user document, instead of it being auto generarte it, if that’s even the case.
Here is the registration code for adding documents to firestore:
let database = Firestore.firestore()
database.collection("users").addDocument(data: [ "name" :name, "email" : email ]) { (error) in
if error != nil {
//
}
an here is a snapshot of my firestore users collection
When creating a user;
Auth.auth().createUser(withEmail: email, password: password) { authResult, error in
// ...
}
At first you can only save email and password. (For now, that's how I know.)
But after you create the user, you can update the user's name.
let changeRequest = Auth.auth().currentUser?.createProfileChangeRequest()
changeRequest?.displayName = displayName
changeRequest?.commitChanges { error in
// ...
}
Use userUID when saving user information in Firestore.
If you drop the document into firebase, it will create it automatically. But if you save the user uid, it will be easy to access and edit.
func userSave() {
let userUID = Auth.auth().currentUser?.uid
let data = ["name": "ABCD", "email": "abcd#abcd.com"]
Firestore.firestore().collection("users").document(userUID!).setData(data) { error in
if error != nil {
// ERROR
}
else {
// SUCCESSFUL
}
}
}
If you are saving user information in Firestore, you can retrieve information very easily.
func fetchUser() {
let userUID = Auth.auth().currentUser?.uid
Firestore.firestore().collection("users").document(userUID!).getDocument { snapshot, error in
if error != nil {
// ERROR
}
else {
let userName = snapshot?.get("name")
}
}
}
For more detailed and precise information: Cloud Firestore Documentation
If you see missing or incorrect information, please warn. I will fix it.
There's a distinction between a Firebase User property displayName and then other data you're stored in the Firestore database.
I think from your question you're storing other user data (a name in this case) in the Firestore database. The problem is where you're storing it is not the same as where you're reading it from.
According to your code here's where it's stored
database.collection("users").addDocument(data: [ "name" :name,
which looks like this
firestore
users
a 'randomly' generated documentID <- not the users uid
name: ABCD
email: abcd#email.com
and that's because addDocument creates a documentID for you
Where you're trying to read it from is the actual users UID, not the auto-created documentID from above
Firestore.firestore().collection("users").document(userUID!)
which looks like this
firestore
users
the_actual_users_uid <- the users uid
name: ABCD
email: abcd#email.com
The fix it easy, store the data using the users uid to start with
database.collection("users").document(uid).setData(["name" :name,
I'm trying to add additional information to my cloud function so that way my Stripe customer has all of the data saved in the Firebase Database. However, my question is how can I implement the constants in my cloud function correctly so the information uploads correctly? Without the fullname, username, and profileImage in my cloud function and my registration function in the functions section, it creates the Stripe customer. How do I structure the constants for those three fields so they can upload as well? Or should I create an email and password registration screen, so I can create the stripeID, then create another screen for additional information to add to the reference? Thank you!
Cloud Function:
exports.createStripeCustomer = functions.https.onCall( async (data, context) => {
const email = data.email
const uid = context.auth.uid
const fullname = context.auth.uid.fullname
const username = context.auth.uid.username
const profileImage = context.auth.uid.profileImage
if (uid === null) {
console.log('Illegal access attempt due to unauthenticated attempt.')
throw new functions.https.HttpsError('internal', 'Illegal access attempt')
}
return stripe.customers.create({
email : email,
fullname : fullname,
username : username,
profileImage : profileImage
}).then( customer => {
return customer["id"]
}).then( customerId => {
admin.database().ref("customers").child(uid).set(
{
stripeId: customerId,
email: email,
fullname: fullname,
username: username,
profileImage: profileImage,
id: uid
}
)
}).catch( err => {
throw new functions.https.HttpsError('internal', 'Unable to create Stripe customer.')
})
})
AuthService Function:
static func createCustomer(credentials: CustomerCredentials, completion: #escaping(DatabaseCompletion)) {
guard let imageData = credentials.profileImage.jpegData(compressionQuality: 0.3) else { return }
let filename = NSUUID().uuidString
let storageRef = STORAGE_REF.reference(withPath: "/customer_profile_images/\(filename)")
storageRef.putData(imageData, metadata: nil) { (meta, error) in
if let error = error {
debugPrint(error.localizedDescription)
return
}
storageRef.downloadURL { (url, error) in
guard let profileImageUrl = url?.absoluteString else { return }
Auth.auth().createUser(withEmail: credentials.email, password: credentials.password) { (result, error) in
if let error = error {
debugPrint(error.localizedDescription)
return
}
guard let uid = result?.user.uid else { return }
let values = ["email" : credentials.email,
"fullname" : credentials.fullname,
"username" : credentials.username,
"uid" : uid,
"profileImageUrl" : profileImageUrl] as [String : Any]
CustomerDataService.saveCustomerData(uid: uid, fullname: credentials.fullname, email: credentials.email,
username: credentials.username, profileImagUrl: profileImageUrl)
REF_CUSTOMERS.child(uid).setValue(values, withCompletionBlock: completion)
}
}
}
}
Registration Function:
#objc func handleCreateAccount() {
guard let profileImage = profileImage else {
self.simpleAlert(title: "Error", msg: "Please select a profile image.")
return
}
guard let email = emailTextField.text?.lowercased() , email.isNotEmpty ,
let fullname = fullnameTextField.text , fullname.isNotEmpty ,
let username = usernameTextField.text?.lowercased() , username.isNotEmpty ,
let password = passwordTextField.text , password.isNotEmpty ,
let confirmPassword = confirmPasswordTextField.text , confirmPassword.isNotEmpty else {
self.simpleAlert(title: "Error", msg: "Please fill out all fields.")
return
}
if password != confirmPassword {
self.simpleAlert(title: "Error", msg: "Passwords don't match, please try again.")
return
}
showLoader(true, withText: "Registering Account")
let credentials = CustomerCredentials(email: email, fullname: fullname, username: username,
password: password, profileImage: profileImage)
AuthService.createCustomer(credentials: credentials) { (error, ref) in
if let error = error {
Auth.auth().handleFireAuthError(error: error, vc: self)
self.showLoader(false)
return
}
Functions.functions().httpsCallable("createStripeCustomer").call(["email": credentials.email,
"fullname": credentials.fullname,
"username": credentials.username,
"profileImage": credentials.profileImage]) { result, error in
if let error = error {
Auth.auth().handleFireAuthError(error: error, vc: self)
self.showLoader(false)
return
}
}
self.showLoader(false)
guard let window = UIApplication.shared.windows.first(where: { $0.isKeyWindow }) else { return }
guard let tab = window.rootViewController as? MainTabController else { return }
tab.setupNavigationControllers()
self.handleDismissal()
}
}
To complete what I was trying to accomplish, I created a screen for customers to create an e-mail and password. This way the StripeID could be created, and then I created another screen to add the full name, username and profile image, and updated the database reference.
I am not familiar with the new Firebase. How do I create new users? The code below I Signup and auth new user. If I need to create this new user under "Customers" in Firebase Database, what code do I need to add? Thanks!
FIRAuth.auth()?.createUserWithEmail(email, password: password, completion: { (user, err) in
if err != nil {
self.showAlert("Can't Register", msg: "Please enter email and password")
} else {
NSUserDefaults.standardUserDefaults().setValue(user?.uid, forKey: "uid")
FIRAuth.auth()?.signInWithEmail(email, password: password, completion: { (user, error) in
})
self.performSegueWithIdentifier("toSecondVC", sender: self)
}
})
FIRAuth.auth()?.createUserWithEmail(email, password: password, completion: { (user, err) in
if err != nil {
self.showAlert("Can't Register", msg: "Please enter email and password")
} else {
NSUserDefaults.standardUserDefaults().setValue(user?.uid, forKey: "uid")
FIRDatabase.database().reference().child("Customers").setValue([user!.uid : "true"])
//Or if you want to save the users Details too:-
/*
FIRDatabase.database().reference().child("Customers").setValue([user!.uid : ["email" : email,"password" : password]])
*/
self.performSegueWithIdentifier("toSecondVC", sender: self)
}
})
Also might i suggest reading this : Firebase iOS - Save Data
The answer above is correct, but I'd recommend doing it this way:
var values = [Any : Any] // This is a dictionary where you can store user details
values["userUID"] = // user's uid
values["usersName"] = // user's name
// etc.
let customerRef = databaseReference.child("Customers") // You could also add a child with the user's UID in order to identify each user
customerRef.updateChildValues(values, withCompletionBlock: { (error, ref) in
if error != nil {
// display error.code and error.localizedDescription to user if needed
} else if ref != [] {
// Success!
// If needed, save the user to NSUserDefaults and perfrom a segue to another ViewController
} else {
// There was an error, but not sure what happened. Let the user know how they can fix the problem (maybe add the details to the database later)
}
})
below I just typed is error showing "Cannot cover value of type '(,) -> Void' to expected argument type '(NSERROR!) -> Void!)'
on this line of code: what would be wrong?
FIREBASE_REF.createUser(email, password: password, withCompletionBlock: {(error,authData) -> Void in
#IBAction func creatAccountAction(sender: AnyObject) {
let email = self.emailTextField.text
let password = self.passwordTextField.text
if email != "" && password != ""
{
FIREBASE_REF.createUser(email, password: password, withCompletionBlock: {(error,authData) -> Void in
if error != nil {
FIREBASE_REF.authUser(email, password: password, withCompletionBlock: { (error, authData) -> Void in
if error == nil {
NSUserDefaults.standardUserDefaults().setValue(authData.uid, forKey: "uid")
}
else {
print (error)
}
})
}
else {
print (error)
}
}
)}
else
Try this:
FIREBASE_REF.createUser(email, password: password, withCompletionBlock: {(error) -> Void in
This block has probably only one parameter
There are two options for creating a user, one just creates it and returns an error if it fails (withCompletionBlock), the other also returns the authData (withValueCompletionBlock): that's the one you want.
myRootRef.createUser(email, password: pw, withValueCompletionBlock: { error, result in
if error != nil {
print("error creating user")
} else {
let uid = result["uid"] as! String
NSUserDefaults.standardUserDefaults().setValue(uid, forKey: "uid")
//pass the parameters to another function to auth the user
self.authUserWithAuthData( email, password: pw )
}
})
After a user has logged in and if they later decide they want to change their username they have the ability to do so. when I save the record I catch the parse errors for username already exists and email already exists.
The issue I have is if the user decides not change their info after that the app is keeping the "BAD Username" in the cache and shows it all over the app.
I have tried calling PFUser.currentUser().fetch() as you will see below to refresh it from what is in the DB but to no avail.
The only thing that works is signing out and back in to get it back to whats in the DB.
Any ideas?
heres my code to catch the username error and attempt to refresh the PFUser
func saveUser() {
let username = nameField.text
let firstname = firstNameField.text! as String
let lastname = lastNameField.text! as String
let profilemsg = profileMessage.text! as String
let email = emailField.text! as String
if username!.characters.count > 0 {
let user = PFUser.currentUser()
if let user = user {
user["username"] = username
user["first"] = firstname
user["last"] = lastname
user["greeting"] = profilemsg
user["email"] = email
}
user!.saveInBackgroundWithBlock {(success: Bool, error: NSError?) -> Void in
if (success) {
ProgressHUD.showSuccess("Saved")
} else {
let errorCode = error!.code
switch errorCode {
case 202:
ProgressHUD.showError("Username " + username! + " already taken")
self.nameField.text = nil
do
{
try PFUser.currentUser()!.fetch()
print("User refreshed")
}
catch{
}
break
case 203:
ProgressHUD.showError("E-mail " + email + " already taken")
self.emailField.text = nil
do
{
try PFUser.currentUser()!.fetch()
print("User refreshed")
}
catch{
}
break
default:
ProgressHUD.showError("Network Error")
break
}
}
}
} else {
ProgressHUD.showError("Userame field must not be empty")
}
}
There is no need on creating a new instance of user to save the currentUser.
Just make the changes on the currentUser as it follows:
PFUser.currentUser()!["username"] = username
...
and then save it:
PFUser.currentUser()!.saveInBackgroundWithBlock {(...)}