Firebase user display name causing crash - ios

I'm running into a bug involving Firebase users displayName value. Here is my code:
if let user = user {
if user.displayName == nil || user.displayName == "" {
self.performSegue(withIdentifier: "AddName", sender: nil)
} else {
let displayName = user.displayName!.replacingOccurrences(of: " ", with: "_")
if let fcmToken = UserDefaults.standard.object(forKey: "firToken") as? String {
if !fcmToken.isEmpty {
ref.child("cities").child(driversCurrentCity).child("loggedInDrivers").child(displayName).child("fcmToken").setValue(fcmToken)
ref.child("cities").child(driversCurrentCity).child("loggedInDrivers").child("fcmTokens").child(fcmToken).setValue(true)
ref.child("users").child((FIRAuth.auth()?.currentUser?.uid)!).child("fcmToken").setValue(fcmToken)
UserDefaults.standard.set(true, forKey: "loginCompleted")
self.performSegue(withIdentifier: "LoggedInSuccess", sender: nil)
}
}
}
This is the code that I use to set the displayName:
I'm using this function from Firebase to set the displayName:
let changeRequest = Auth.auth().currentUser?.createProfileChangeRequest()
changeRequest?.displayName = displayName
changeRequest?.commitChanges { (error) in
// ...
}
For some users, everything works fine. However, some users experience a crash at the line
ref.child("cities").child(driversCurrentCity).child("loggedInDrivers").child(displayName).child("fcmToken").setValue(fcmToken)
I'm getting this error:
Fatal Exception: InvalidPathValidation
(child:) Must be a non-empty string and not contain '.' '#' '$' '[' or ']'
I haven't been able to replicate it lately, but when I
first started debugging the issue it was showing that displayName was some long string involving gestureRecognizer. It seems like some code when the sign in button is pressed is being applied to the displayName variable. I know this question is kind of vague but I appreciate any help!

Related

Login credentials missing in swift UserDefaults

I have an application which uses a rest api for authentication. The problem I am facing now is that I save user's token in my UserDefaults and username too because those are the two main parameters needed to get user details. so if the application is closed by the user he should still be able to view the view his profile when he opens the application back but instead the profile returns empty details. this is the UserDefaults codes that I have
let defaults = UserDefaults.standard
var isLoggedIn : Bool {
get {
return defaults.bool(forKey: LOGGED_IN_KEY)
}
set {
defaults.set(newValue, forKey: LOGGED_IN_KEY)
}
}
//Auth Token
var authToken: String {
get {
return defaults.value(forKey: TOKEN_KEY) as? String ?? ""
}
set {
defaults.set(newValue, forKey: TOKEN_KEY)
}
}
var userUsername: String {
get {
return defaults.value(forKey: USERNAME_KEY) as? String ?? ""
}
set {
defaults.set(newValue, forKey: USERNAME_KEY)
}
}
I have no idea why it isnt retrieving the user data.
My second question is when I logout the user, all the users details are cleared as expected but the moment I try loging in with a different user, the new user's authToken and details gets printed in the console but the user profile returns the profile of the previous person. which is not supposed to be. my code is shown below
func logoutUser() -> Void {
pk = 0
username = ""
email = ""
firstName = ""
lastName = ""
AuthService.instance.isLoggedIn = false
AuthService.instance.authToken = ""
AuthService.instance.userUsername = ""
}
#IBAction func logoutPressed(_ sender: Any) {
UserDataService.instance.logoutUser()
dismiss(animated: true, completion: nil)
}
I would also like to add that when i run the api using postman i get a response that "detail": "Signature has expired." so i had to input the new token in the header so it displays the user details again
enum SettingKeys: String {
case authToken
//...
}
struct Settings {
static var authToken: String? {
get { return UserDefaults.standard.string(forKey: SettingKeys.authToken.rawValue) }
set(value) { UserDefaults.standard.set(value, forKey: SettingKeys.authToken.rawValue) }
}
static func deleteAll(exclude: [SettingKeys] = []) {
let saveKeys = exclude.map({ $0.rawValue })
for key in UserDefaults.standard.dictionaryRepresentation().keys {
if !saveKeys.contains(key) {
UserDefaults.standard.removeObject(forKey: key)
}
}
}
}
I recommend storing keys as Enum, because then u can use it like that:
//Read
if let token = Settings.authToken {
//do something
}
//Write
Settings.authToken = "123456"
//Delete settings
Settings.deleteAll()
//or choose what to leave
Settings.deleteAll(exclude: [.authToken])
And it's worth to mention that defaults.synchronize() is deprecated.

Why are these issues arising with my swift code?

I am trying to implement a login through Parse, however, I am having a number of problems.
The first two If statements work but then perform a segue back to the first screen, even though there is no segue on my storyboard that even does this. So why is the alert popping up doing this? It doesn't wait or let me press OK.
When I sign up a user, it registers them (given the condition is met) to Parse but doesn't perform the segue which I have implemented? Fyi it doesn't show the alert error either.
On parse, there are frequently users signed up with random letters. I don't know how or why this happens.
Thank you in advance.
Jake
if passwordField.text == "" || confirmPasswordField.text == "" || usernameField == "" || lastNameField.text == "" || firstNameField.text == "" {
doAlert(title: "Incomplete Form", message: "You have not filled in all the forms")
} else if passwordField.text != confirmPasswordField.text {
doAlert(title: "Password mismatch", message: "Your passwords do not match")
} else {
let user = PFUser()
user.username = usernameField.text
user.password = passwordField.text
user["First Name"] = firstNameField.text
user["Last Name"] = lastNameField.text
user.signUpInBackground(block: { (success, error) in
if error != nil {
var displayMessage = "Please try again later"
if let errorMessage = error?.userInfo["error"] as? String {
displayMessage = errorMessage
} } else {
self.performSegue(withIdentifier: "RegisteredViewController", sender: self)
}
})
}

Firebase - Unable to execute call to backend

I am working on a function that handles user registration and in the process, check if the selected username entered by the user is taken or not to inform the user to select a different one. I have the below code to accomplish this scenario:
#IBAction func proceedPressed(sender: AnyObject) {
/**********************Perform Validation************************/
if(self.emailTxtField.text != "" && self.passwordTxtField.text != "")
{
print("Email and Password not empty")
self.usernameValidation({(result) -> Void in
if(result == false)
{
print("Result False")
self.usernameErrorLabel.text = "Username Taken"
}else{
print("Result True")
//Username is available...Proceed
self.usernameErrorLabel.text = ""
FIRAuth.auth()?.createUserWithEmail(self.emailTxtField.text!, password: self.passwordTxtField.text!) { (user, error) in
if(error == nil)
{
print("Creating User with Email")
/*Create the user object as submitted*/
self.dbReference.child("users").child(user!.uid).setValue(["username": self.emailTxtField.text!,"name":self.nameTxtField.text!, "email":self.emailTxtField.text!, "mobile":self.mobileTxtField.text!, "homeAddress":"N", "workAddress":"N", "otherAddress":"N", "profilePictureRef":"N","telephone":"0","friendsCount":0, "retailersCount":0])
}else{
print("Error occured: \(error?.description)")
}
}//end of createUserWithEmail
}
})
}else{
print("Error: Email or Password field is empty")
}
}
and to check the username:
func usernameValidation(completion: (result: Bool) -> Void)
{
print("Username is: \(self.usernameTxtField.text!)")
dbReference.child("usernamesTaken").queryOrderedByValue().queryEqualToValue(self.usernameTxtField.text!).observeEventType(.Value, withBlock: { (snapshot: FIRDataSnapshot!) -> Void in
print(snapshot.childrenCount)
if(snapshot.childrenCount == 0)
{
print("result is true in username validation")
//Username Available
completion(result:true)
}else{
print("result is false in username validation")
//Username Taken
completion(result:false)
}
})
}
The problem with the above is that the full code doesn't seem to execute. When button pressed, I get the following messages in console:
- Email and Password not empty
- Username is: [value entered in usernameTxtField.text
and then nothing more. Although I wrote many print statements to try and see where this is stopping, but this is the furthest the code went in terms of printing the statements.
Is there something wrong here that I am missing out?
Thanks in advance.
I did some more testing and then discovered the issue through the xcode console. I copied the following from the firebase website to test fetching the data:
ref.child("users").child(userID!).observeSingleEventOfType(.Value, withBlock: { (snapshot) in
// Get user value
let username = snapshot.value!["username"] as! String
let user = User.init(username: username)
// ...
}) { (error) in
print(error.localizedDescription)
}
The above showed an error that is "Permission Denied". Following that I edited the Rules in the database section in the console and allowed .read and .write and that did it. I thought I would post the details just in case someone else gets stuck.

"cannot convert value of type" - error in Firebase Swift

I am trying to create a user in Firebase with an email & password and upon successful creation of the user record, I would go and update tree with additional attributes as child nodes of the user's UID. Here is what I have in code:
#IBAction func registerPressed(sender: AnyObject) {
let connection = Firebase(url:"https://something.firebaseio.com")
connection.createUser(self.emailTxtField.text, password: self.passwordTxtField.text,
withValueCompletionBlock: { error, result in
if error != nil {
// There was an error creating the account
print("Error Creating Account...Please try again")
} else {
let uid = result["uid"] as? String
print("Successfully created user account with uid: \(uid)")
/*After creating the user, we want to update the tree with other attributes*/
let userAdditionalDetails = ["name": self.nameTxtField.text, "mobile": self.mobileTxtField.text, "username": self.usernameTxtField.text]
let usersRef = connection.childByAppendingPath("users")
let users = ["\(uid)": userAdditionalDetails]
usersRef.setValue(users)
}
})
}
Although am not sure if the above will work or not (since this my first work with Firebase SDK), I am getting the following error:
cannot convert value of type “[String: [String : String?]]” to
expected argument type "AnyObject!"
I am unable of course to build the project due to the above error being triggered at line:
usersRef.setValue(users)
Any idea why this is happening and how to solve it?
Thanks in advance.
Found the solution and just having it here for someone else's future reference:
#IBAction func registerPressed(sender: AnyObject) {
let connection = Firebase(url:"https://something.firebaseio.com")
connection.createUser(self.emailTxtField.text, password: self.passwordTxtField.text,
withValueCompletionBlock: { error, result in
if error != nil {
// There was an error creating the account
print("Error Creating Account...Please try again")
} else {
let uid = result["uid"] as? String
print("Successfully created user account with uid: \(uid)")
/*After creating the user, we want to update the tree with other attributes*/
let userAdditionalDetails = ["name": "\(self.nameTxtField.text!)", "mobile": "\(self.mobileTxtField.text!)", "username": "\(self.usernameTxtField.text!)"]
let usersRef = connection.childByAppendingPath("users")
let users = ["\(uid)": userAdditionalDetails]
usersRef.setValue(users)
}
})
}
The catch is that when initializing userAdditionalDetails, should include the outlets variables in string format. E.g.
"username": "\(username.text!)"
This worked and all objects reflected correctly in backend.
In the call to usersRef.setValue(users), the error message states that the parameter is expected to be of type AnyObject!. Therefore, you must cast users to be of type AnyObject!:
let users = ["\(uid)": userAdditionalDetails]
usersRef.setValue(users as? AnyObject)

NSUserDefaults Loosing Value in Swift

Update: I've tried changing setValue to setObject, and the same error occurred.Upon further investigation with breakpoints and the LLDB, they are nil before the controller is even presented. I'm not saving them right.
I'm trying to simply save a couple of strings of text, and display them on another view using Swift. I'm not sure why I'm having such a hard time. Here is how I'm trying to accomplish this:
VC1
#IBAction func registerTapped(sender : AnyObject)
// Save the login information
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setValue(username.text, forKey: "username")
defaults.setValue(password.text, forKey: "password")
if firstName.text.isEmpty == false {
defaults.setValue(firstName.text, forKey: "firstname")
}
if lastName.text.isEmpty == false {
defaults.setValue(lastName.text, forKey: "lastname")
}
let profileView = ProfileViewController()
self.presentViewController(profileView, animated: true, completion: nil)
}
}
Cool. That looks like the correct way to save strings in UITextFields based upon my research. So, I open up VC2 and try to load the saved text into the new UITextField's, like so:
override func viewDidLoad() {
super.viewDidLoad()
let defaults = NSUserDefaults.standardUserDefaults()
username.text = defaults.stringForKey("username")
password.text = defaults.stringForKey("password")
if let first = defaults.stringForKey("firstname")
{
firstName.text = first
}
if let last = defaults.stringForKey("lastname") {
lastName.text = last
}
}
I get the crash fatal error: unexpectedly found nil while unwrapping an Optional value. I've been digging through tutorials for hours and can't for the life of me figure out what I am doing wrong.
Is it because it an an optional? This is my LLDB output:
Your issue has nothing to do NSUserDefaults, whats nil are your labels username, password, etc. in your second controller.
You should add a segue to your button (the one with registerTapped) to show the second controller and remove the last two lines in registerTapped.
Break your code into steps and debug each one. Your code would crash if your outlet is nil or if the key/value pair doesn't exist. Check that both username and password (The text fields) are not nil, as well as that the defaults results aren't nil:
var text: String?
text = defaults.stringForKey("username")
if let text = text
{
if let username = username
{
username.text = text
}
else
{
println("username field is nil!")
}
}
else
{
println("defaults stringForKey("username") = nil!")
}
text = defaults.stringForKey("password")
if let text = text
{
if let password = password
{
password.text = text
}
else
{
println("password field is nil!")
}
}
else
{
println("defaults stringForKey("password") = nil!")
}

Resources