TextField input validation pattern - swift

In a ViewController I have three text fields (mail, password, repeat password). Before sending data to the server I do a little validation (check if text exists, if mail is valid, etc.).
I do this way:
let email = emailTextfield.text
let password = passwordTextfield.text
let repeatPassword = repeatPasswordTextfield.text
if let e = email {
if let p = password {
if let rp = repeatPassword{
if(e.isEmpty || p.isEmpty || rp.isEmpty){//cut mail validation...
The question is: is this the best way to do it? Is there any better (maybe more compact) way? Thanks in advance.

Starting from Swift 2.0 or so, you no longer need to construct an optional binding pyramid of doom, but can use several bindings (as well as boolean conditionals) in the same if statment. E.g.:
if let email = emailTextfield.text, !email.isEmpty,
let password = passwordTextfield.text, !password.isEmpty,
let repeatPassword = repeatPasswordTextfield.text, !repeatPassword.isEmpty {
// proceed only for all non-nil and non-empty above ...
}
These will naturally short-circuit for first failed binding/false conditional met.

I am not sure but I can see two solutions:
-The first one is clearer:
if let e = email, let p = password, let rp = repeatPassword {
if e.isEmpty || p.isEmpty || rp.isEmpty {
// do your things
}
}
-the second one is more compact:
if email != nil, password != nil, repeatPassword != nil, (email!.isEmpty || password!.isEmpty || repeatPassword!.isEmpty) {
// do the things force unwrapping every variable
}
the second solution works because if email or password or repeatPassword is nil, the compiler won't continue reading conditions, and consequently won't crash reading for example repeatPassword!.isEmpty as nil.isEmpty
To build on #dfri 's answer, I can think of this solution (non tested) :
if let e = email, let p = password, let rp = repeatPassword, (e.isEmpty, p.isEmpty, rp.isEmpty) {
// cut mail validation
}
the last one, if works, is obviously the most elegant solution and I'll delete it as soon as #dfri updates his solution to comply with your answer :)

if let email = emailTextfield.text where !email.isEmpty,
let password = passwordTextfield.text where !password.isEmpty,
let repeatPassword = repeatPasswordTextfield.text where !repeatPassword.isEmpty {
// Go for it.
}

You can just do
if email?.isEmpty || password?.isEmpty || repeatPassword?.isEmpty { //break }
Don't worry about nil value, let it as optionnal and everything will be fine.

Related

Document ID retrieval

this is supposed to take the user ID from the result!.user.uid and store it a variable or function in order for me to use it again.
the problem is that I dont know how to get it to store the value outside of this function.
Ive tried to make it store to a variable outside of the initial button function, and Ive also tried to return it outside of the function by removing a part of the code which made it become a void. Im not sure where i need to go/what else I can try and do in order to fix this problem.
If anybody know how do I retrieve my document ID from this code your help would be greaty appreciated
#IBAction func NextButtonTapped(_ sender: Any) {
//validate the fileds
let Error = validateFields()
if Error != nil {
// there is somthing wrong with the fields show error message
showError(Error!)
}
else {
// create cleaned versions of the data
let Password = PasswordTextField.text!.trimmingCharacters(in: .whitespacesAndNewlines)
let Email = EmailTextField.text!.trimmingCharacters(in: .whitespacesAndNewlines)
let Firstname = FirstnameTextField.text!.trimmingCharacters(in: .whitespacesAndNewlines)
let Lastname = LastnameTextField.text!.trimmingCharacters(in: .whitespacesAndNewlines)
let Age = AgeTextField.text!.trimmingCharacters(in: .whitespacesAndNewlines)
// create the user
Auth.auth().createUser(withEmail: Email, password: Password) { (results, Err) in
// check for errors
if Err != nil {
// there was an error creating the user
self.showError("Error creating user")
}
else {
// user was created succesfully store first and last name
let db = Firestore.firestore()
db.collection("users").document(results!.user.uid).setData(["first name":Firstname, "last name":Lastname, "age":Age, "uid":results!.user.uid]) { (Error) in
if Error != nil {
// show error message
self.showError("error saving user data")
}
//showing users document id
}
//transition to the home screen
self.transitionToHome()
}
}
}
}
I have no idea what to do any help would be amazing,
thank you very much!!!!
Define a uid variable outside of the IBAction function like so.
var uid: String? = nil
Then, within the createUser function
self.uid = results!.user.uid

Swift 3: Determining if an email domain is valid with Firebase

I'm building a user registration that connects to firebase. I am unable to get firebase to discern if an email domain is valid or not so I want to provide an array of valid well known email domains which users can have to register for my app. I want to error handle for the occurence of an invalid email domain, so I need to be able to compare the end of the email the user entered with the array of valid emails I will allow. How can I check to confirm that ex: 'apples#gmail.com' is valid but ex: 'apples#gnail.com' is not valid?
let emails: Array = ["gmail.com", "yahoo.com", "comcast.net", "hotmail.com", "msn.com", "verizon.net"]
#IBAction func nextBtnPressed(_ sender: Any) {
let ref: DatabaseReference!
ref = Database.database().reference()
if let email = emailTextField.text, let pwd = passwordTextField.text, let firstName = firstNameTextField.text, let lastName = lastNameTextField.text, let dob = birthdayTextField.text {
if pwd != self.reEnterPassTextField.text {
errorMessageLbl.text = "Passwords do not match"
errorMessageLbl.isHidden = false
return
} else if firstName == "" || lastName == "" || dob == ""{
errorMessageLbl.text = "Cannot leave fields blank"
errorMessageLbl.isHidden = false
return
} else if email.characters.elementsEqual([emails]) {
print("Failure")
One of the way you can do this:
let validDomains = ["gmail.com", "yahoo.com", "comcast.net", "hotmail.com", "msn.com", "verizon.net"]
let emailTextBlockText = "example#gmail.com"
if let domain = emailTextBlockText.components(separatedBy: "#").last, validDomains.contains(domain) {
// Entered email has valid domain.
}

Deleting PFUser objects from database in swift

I just started working with servers in swift and I'm using parse server to store a database of users when they create an account for my app. I have this function called sign up that is linked to a sign up button which works fine and properly stores user's info into my parse database.:
#IBAction func signUp(_ sender: AnyObject) {
//I first check to see if the users left any of the fields blank
if firstName.text == "" || lastName.text == "" || email.text == "" || userName.text == "" || password.text == "" {
createAlert(title: "Error in form", message: "Please fill in all text fields")
//If everything is filled in
}else{
let user = PFUser()
user.username = userName.text
user["firstname"] = firstName.text
user["lastname"] = lastName.text
user.email = email.text
user.password = password.text
user.signUpInBackground(block: { (success, error) in
if error != nil {
var displayErrorMessage = "Please try again later."
if let errorMessage = (error! as NSError).userInfo["error"] as? String {
displayErrorMessage = errorMessage
}
self.createAlert(title: "Signup error", message: displayErrorMessage)
}else{
print("User signed up")
}
})
}
}
Can anyone help me write a function that deletes a specified user or many loops through all users and deletes them.
Thanks
Don't put that code on a client. Yuck.
Deleting a user is something you want to safeguard very carefully. Put that in cloud code if you really need it, and do validation to ensure it's used properly.
I.e., a user should only be able to make the request to delete their own user. Make a cloud code function that has the parameter userId, and check that param against request.user.id. If they don't match, return an error. Otherwise you can call the destroy method on that User.
You should also put a CLP in place to only allow the master key to delete a User object.

Unique usernames in Firebase

I have been trying to implement Chris’ answer here: Can I make Firebase use a username login process? for the Facebook login but I can’t seem to get my head around it.
So far I’ve tried to set conditions on the textField but as Firebase observer works asynchronously, the conditions to check if the username exists in the database won’t work.
let usernameString = usernameTextField.text
let uid = FIRAuth.auth()?.currentUser?.uid
ref.runTransactionBlock({ (currentData: FIRMutableData) -> FIRTransactionResult in
if var post = currentData.value as? [String : AnyObject], let uid = FIRAuth.auth()?.currentUser?.uid {
let usernamesDictionary = post["usernames"] as! NSDictionary
for (key, _) in usernamesDictionary {
if key as? String == usernameString {
print("username not available: \(key)")
}
else if usernameString == "" {
print("Uh oh! Looks like you haven't set a username yet.")
}
else if key as? String != usernameString {
print("username available: \(key)")
print("All set to go!")
let setValue: NSDictionary = [usernameString!: uid]
post["usernames"] = setValue
currentData.value = post
}
}
return FIRTransactionResult.successWithValue(currentData)
}
return FIRTransactionResult.successWithValue(currentData)
}
Then I tried creating /usernames/ node in the database and set up rules as:
{
"rules": {
"usernames": {
".read": "auth != null",
".write": "newData.val() === auth.uid && !data.exists()"
}
}
}
Now that won’t let me set any username to the database. I get confused in creating rules but my whole point is that I need a sign up flow with the username data that’s unique for each user in the database.
While trying every answer I found in related posts, what worked for me the easy way i.e. without making Firebase rules play a part in it or creating a separate usernames node in the database was to not put an if/else condition inside the Firebase observer but instead to use the exists() method of FIRDataSnapshot.
Now here’s the trick, while I did try only the exists() method with a simple observer but that did not help me. What I did was first query usernames in order, then match the username with queryEqualToValue to filter the query:
refUsers.queryOrderedByChild("username").queryEqualToValue(usernameString).observeSingleEventOfType(.Value , withBlock: {
snapshot in
if !snapshot.exists() {
if usernameString == "" {
self.signupErrorAlert("Uh oh!", message: "Looks like you haven't set a username yet.")
}
else {
// Update database with a unique username.
}
}
else {
self.signupErrorAlert("Uh oh!", message: "\(usernameString!) is not available. Try another username.")
}
}) { error in
print(error.localizedDescription)
}
}
This is the first time out of most of the answers here that worked for me. But for now, I don’t know if this would scale. Post your experiences and best practices. They’ll be appreciated.

SWIFT: do I have someone set a record with different text every time?

So I made a chatroom and when someone sends a message they also add a Subscription in my cloud kit database but the problem is there cant be more then one of the same name that is a subscription and I want them to be able to set more subscriptions then one. Here is some code:
func setupCloudKitSubscription () {
let userDefaults = NSUserDefaults.standardUserDefaults()
if userDefaults.boolForKey("subscribed") == false {
let predicate = NSPredicate(format: "TRUEPREDICATE", argumentArray: nil)
let subscription = CKSubscription(recordType: "Extra1", predicate: predicate, options: CKSubscriptionOptions.FiresOnRecordCreation)
let notificationInfo = CKNotificationInfo()
notificationInfo.alertLocalizationKey = "New Sweet"
notificationInfo.shouldBadge = true
subscription.notificationInfo = notificationInfo
let publicData = CKContainer.defaultContainer().publicCloudDatabase
publicData.saveSubscription(subscription) { (subscription:CKSubscription?, error:NSError?) -> Void in
if error != nil {
print(error?.localizedDescription)
}else{
userDefaults.setBool(true, forKey: "subscribed")
userDefaults.synchronize()
You see how it says recordType: "Extra1" how can I made the "Extra1" different text every time someone makes a subscription? Thanks!
Your question is not completely clear. I think what you wanted to ask is that you want the subscription to send you a different message with each notification.
You could set it to display one or more fields of the record. For doing that you should use something like this:
notificationInfo.alertLocalizationKey = "Response: %1$#"
notificationInfo.alertLocalizationArgs = ["responseField"]
Then you also need this in your Localization.Strings file.
"Response: %1$#" = "Response: %1$#";