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 {(...)}
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 am testing signing up a user with Firebase via Sign In With Apple. I have the basic set up which can be viewed here.
In my Auth.auth().sign(_:) with credential method, I am trying to extract the .fullName from the appleIDCredential to use later to identify the user.
Here's the code in my :didCompleteWithAuthorization delegate method in the func authorizationController(_:):
let credential = OAuthProvider.credential(withProviderID: "apple.com",
idToken: idTokenString,
rawNonce: nonce)
Auth.auth().signIn(with: credential) { (authResult, error) in
if error != nil {
print(error!.localizedDescription)
return
}
if let _ = authResult?.user {
let changeRequest = authResult?.user.createProfileChangeRequest()
changeRequest?.displayName = appleIDCredential.fullName?.description
changeRequest?.commitChanges(completion: { (error) in
if let error = error {
print(error.localizedDescription)
} else {
print("Updated display name: \(Auth.auth().currentUser!.displayName!)")
}
})
}
}
I use the .description get-only method on the .fullName to get the textual representation of the returned credential, which is appropriately the full name of the user at the time of authentication.
But the final displayName includes the literal givenName and familyName prefixes, viz;
givenName: David familyName: Example
I tried splitting the string into an array of four elements, and filtering, but no luck.
I would like to return David Example from the credential, which is the user's name at the point of authentication.
Solution
if let _ = authResult?.user {
let changeRequest = authResult?.user.createProfileChangeRequest()
if let givenName = appleIDCredential.fullName?.givenName,
let familyName = appleIDCredential.fullName?.familyName {
changeRequest?.displayName = "\(givenName) \(familyName)"
}
changeRequest?.commitChanges(completion: { (error) in
if let error = error {
print(error.localizedDescription)
} else {
print("Updated display name: \(changeRequest?.displayName ?? "")")
}
})
}
You can extract the givenName and familyName separately and put them back together as displayName like this:
if let givenName = appleIDCredential.fullName?.givenName,
let familyName = appleIDCredential.fullName?.familyName {
changeRequest?.displayName = "\(givenName) \(familyName)"
}
hello, i need some help with function and or possibly closures in that function. I want my function to check the firestore users collection for duplicate documents (usernames). If a duplicate is found i want to display a message, if a duplicate is not found, create a new user. i have folowing code:
func checkIfUserExists(username: String, completion: #escaping (Bool) -> Void) {
let docRef = db.collection("users").document(username)
docRef.getDocument { (document, error) in
if error != nil {
print(error)
} else {
if let document = document {
if document.exists {
completion(true)
} else {
completion(false)
}
}
}
}
}
and i call the function with:
if let username = textField.text, username.count > 8 { // Username needs to be more then 8 Char
checkIfUserExists(username: username) { (doesExist) in
if doesExist {
print("user exists")
} else {
print("new User can be created")
}
}
} else {
print("Username needs to be more then 8 char")
}
}
It works, but i have the feeling it is not good practice and i'm making detours. Is this the right way to do it ?
I think the way you're doing it now should work well, but another option to prevent you from having to do a read of the database before writing is to use security rules. For example, if this is the structure of your users collection...
users: [
username1: { // doc ID is the username
userid: abcFirebaseUserId, // a field for the uid of the owner of the username
//...etc
}
]
...then you can use the following rules:
match /users/{username} {
allow create: if request.auth.uid != null;
allow update, delete: if resource.data.userId = request.auth.uid;
}
This allows any authenticated user to create a new username, but only the owner of that username can update it or delete it. If you aren't allowing users to change their username, you wouldn't even have to worry about the second rule. Then, in the client, you go right to creating a username, like so:
func createUsername(username: String, completion: #escaping (String?) -> Void) {
guard let userId = Auth.auth().currentUser.uid else {
completion("no current user")
return
}
let docRef = db.collection("users").document(username)
docRef.setData(data:[userId: userId]) { error in
if let error = error {
completion(error.debugDescription)
} else {
completion(nil)
}
}
}
This would write the new username to the database and pass an error to the closure if there is one. If the username already exists, an insufficient permissions error would be present. When checking if the user exists, you could display the error or alert the user however you wanted.
createUsername(username: username) { err in
if let err = err {
print("user exists")
} else {
print("new User has been created")
}
}
Just a suggestion though. I think they way you're doing it now is fine, too!
During SignUp on my app, I want to retrieve information, such as first name, from iCloud,I then want to store this in my own cloud kit database. How do I access user information from iCloud, without having to ask the user themselves for these relevant fields?
I was able to get it working with this in XCode 8 iOS 10 beta 2:
CKContainer.default().requestApplicationPermission(.userDiscoverability) { (status, error) in
CKContainer.default().fetchUserRecordID { (record, error) in
CKContainer.default().discoverUserIdentity(withUserRecordID: record!, completionHandler: { (userID, error) in
print(userID?.hasiCloudAccount)
print(userID?.lookupInfo?.phoneNumber)
print(userID?.lookupInfo?.emailAddress)
print((userID?.nameComponents?.givenName)! + " " + (userID?.nameComponents?.familyName)!)
})
}
}
Use CKContainer.discoverUserIdentity(withUserRecordID:) in combination with CKContainer.fetchUserRecordID to get a CKUserIdentity object for the current user. You can then use a PersonNameComponentsFormatter to get their name from the nameComponents property of the identity.
let container = CKContainer.defaultContainer()
container.fetchUserRecordIDWithCompletionHandler { (recordId, error) in
if error != nil {
print("Handle error)")
}else{
self.container.discoverUserInfoWithUserRecordID(
recordId!, completionHandler: { (userInfo, error) in
if error != nil {
print("Handle error")
}else{
if let userInfo = userInfo {
print("givenName = \(userInfo.displayContact?.givenName)")
print("familyName = \(userInfo.displayContact?.familyName)")
}else{
print("no user info")
}
}
})
}
}
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)
}
})
}
})
}
}
}