I have some multiple textfields, And If all the fields are filled then only I have to call some method else I have to throw alert.
But, even textfields are empty, It is executing condition as false.
if genderTextField.text?.isEmpty == true && weightTextField.text?.isEmpty == true && heightTextField.text?.isEmpty == true {
self.showAlert(withTitle:"Title", withMessage: "Fill all the fields")
} else {
//call some function
}
But, If I print textfields text
po genderTextField.text
▿ Optional<String>
- some : ""
Any suggestions?
Swift 5.2
A more elegant way to do it would be to create an extension on UITextField
extension UITextField {
var isEmpty: Bool {
if let text = self.text, !text.isEmpty {
return false
} else {
return true
}
}
}
And then you check like this:
if genderTextField.isEmpty || weightTextField.isEmpty || heightTextField.isEmpty {
showAlert()
} else {
// do something else
}
Related
This question already has answers here:
How to check if a text field is empty or not in swift
(16 answers)
Checking if textfields are empty Swift
(6 answers)
Closed 4 years ago.
I'm currently working on a project where I use lots of UITextFields. For validation I need to check if the UITextFields are empty. I got a working solution, but it's not that elegant. Maybe someone knows a better way of doing it.
Here is my solution:
// Check if text field is empty
if let text = textField.text, !text.isEmpty {
// Text field is not empty
} else {
// Text field is empty
}
Is there a faster way without unwrapping the text attribute of the text field to find out if it's empty?
Thanks!
How about extending UITextField…
extension UITextField {
var isEmpty: Bool {
if let text = textField.text, !text.isEmpty {
return false
}
return true
}
}
so then…
if myTextField.isEmpty {
}
You can use UIKeyInput property hasText. It works for both UITextField and UITextView:
if textField.hasText {
// Text field is not empty
} else {
// Text field is empty
}
If you would like to check if the text has not only spaces on it:
extension UITextField {
var isEmpty: Bool {
return text?.trimmingCharacters(in: .whitespacesAndNewlines) == ""
}
}
let tf = UITextField()
tf.text = " \n \n "
tf.isEmpty // true
If you have several textfields that you want to check, you could put them all in a guard statement
guard let text1 = textField1.text, let text2 = textField2.text, let text3 = textField3.text, !text1.isEmpty, !text2.isEmpty, !text3.isEmpty else {
//error handling
return
}
//Do stuff
I like to validate each text field depending on the content that should be provided by the user, i.e. emailTextField should contain a valid email address etc. While Ashley Mills answer is convenient, if you regard whitespace " " as text this will return false.
In your case, since you need to validate multiple text fields in the same way, why not extend UITextField as Ashley did with a static class method that can validate each text field passed as an array, in addition to this have other validation methods for each type of text field. Instead of returning a Boolean value I've learned to use guard instead. In this way guard let can be used to check if the validation fails (is nil) and execute the proper code, such as displaying a prompt to the user, or otherwise continue execution.
UITextFieldExtension.swift
import Foundation
import UIKit
extension UITextField {
/// Validates all text field are non-nil and non-empty, Returns true if all fields pass.
/// - Returns: Bool
static func validateAll(textFields:[UITextField]) -> Bool {
// Check each field for nil and not empty.
for field in textFields {
// Remove space and new lines while unwrapping.
guard let fieldText = field.text?.trimmingCharacters(in: .whitespacesAndNewlines) else {
return false
}
// Are there no other charaters?
if (fieldText.isEmpty) {
return false
}
}
// All fields passed.
return true
}
//A function that validates the email address...
func validateEmail(field: UITextField) -> String? {
guard let trimmedText = field.text?.trimmingCharacters(in: .whitespacesAndNewlines) else {
return nil
}
//email addresses are automatically detected as links in i0S...
guard let dataDetector = try? NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue) else {
return nil
}
let range = NSMakeRange(0, NSString(string: trimmedText).length)
let allMatches = dataDetector.matches(in: trimmedText,
options: [],
range: range)
if allMatches.count == 1,
allMatches.first?.url?.absoluteString.contains("mailto:") == true
{
return trimmedText
}
return nil
}
func validateUserName(field: UITextField) -> String? {
guard let text:String = field.text else {
return nil
}
/* 3 to 12 characters, no numbers or special characters */
let RegEx = "^[^\\d!##£$%^&*<>()/\\\\~\\[\\]\\{\\}\\?\\_\\.\\`\\'\\,\\:\\;|\"+=-]+$"
let Test = NSPredicate(format:"SELF MATCHES %#", RegEx)
let isValid = Test.evaluate(with: text)
if (isValid) {
return text
}
return nil
}
/*6 to 16 Characters */
func validatePassword(field: UITextField) -> String?{
guard let text:String = field.text else {
return nil
}
/*6-16 charaters, and at least one number*/
let RegEx = "^(?=.*\\d)(.+){6,16}$"
let Test = NSPredicate(format:"SELF MATCHES%#", RegEx)
let isValid = Test.evaluate(with: text)
if (isValid) {
return text
}
return nil
}
}
Meanwhile, elsewhere...
if (UITextField.validateAll(textFields: [emailTextField, nameTextField])) {
// Do something
}
i have 6 strings (text,year,age,....) i want to filter array based on them ... i want to check before if the string is empty or not .. if not empty then added to the if condition as an && .. but how to do something like this without the need to write many if conditions on each string with each other string ?
i want to avoid doing something like this:
if self.searchtext != "" && self.qualification != ""{
}else if self.searchtext != "" && self.qualification != "" && self.gender != ""{
}else if self.searchtext != "" && self.qualification != "" && self.gender != "" && self.year != ""{
}else if self.searchtext != "" && self.qualification != "" && self.gender != "" && self.year != "" && self.major != ""{
}
....
How to do this?
if self.searchtext != ""{
if user.name.contains(find: self.searchtext) || user.mobile.contains(find: self.searchtext) || user.email.contains(find: self.searchtext){
DispatchQueue.main.async {
self.phones.append(user.mobile)
self.names.append(user.name)
}
}
}else if self.qualification != ""{
if user.qualification.contains(find: self.qualification){
DispatchQueue.main.async {
self.phones.append(user.mobile)
self.names.append(user.name)
}
}
}else if self.gender != ""{
if user.gender.contains(find: self.gender){
DispatchQueue.main.async {
self.phones.append(user.mobile)
self.names.append(user.name)
}
}
}else if self.year != ""{
if user.graduateYear.contains(find: self.year){
DispatchQueue.main.async {
self.phones.append(user.mobile)
self.names.append(user.name)
}
}
}else if self.major != ""{
if user.specialization.contains(find: self.major){
DispatchQueue.main.async {
self.phones.append(user.mobile)
self.names.append(user.name)
}
}
}else if self.city != ""{
if user.city.contains(find: self.city){
DispatchQueue.main.async {
self.phones.append(user.mobile)
self.names.append(user.name)
}
}
}
}
This is what i have working .. but this works as an OR when i want it to be like AND for all none empty strings
Here's a paragraph copied from LinkedIn's style guide
Using guard Statements
3.11.1 In general, we prefer to use an "early return" strategy where applicable as opposed to nesting code in if statements. Using guard statements for this use-case is often helpful and can improve the readability of the code.
// PREFERRED
func eatDoughnut(at index: Int) {
guard index >= 0 && index < doughnuts.count else {
// return early because the index is out of bounds
return
}
let doughnut = doughnuts[index]
eat(doughnut)
}
// NOT PREFERRED
func eatDoughnut(at index: Int) {
if index >= 0 && index < doughnuts.count {
let doughnut = doughnuts[index]
eat(doughnut)
}
}
It's what I personally do, and it's a good convention to avoid nested if's.
In your case, you can do a couple of things. First and foremost, use .isEmpty instead of comparing the string to an empty string (""). If your only intent is to check if all your strings are empty, you can accomplish that like this:
let strings = [searchtext, qualification, ...]
guard strings.filter({ $0.isEmpty }).count == 0 else {
return
}
// Code that only works if all fields have values
We can certainly simplify things a lot.
I would start by wrapping search criteria into an object:
struct UserSearchCriteria {
var searchText = ""
var gender = ""
var qualification = ""
...
init() {}
}
Then you can use one variable instead of your X variables:
var searchCriteria = UserSearchCriteria()
then you can add a simple matching method:
extension UserSearchCriteria {
func matchesUser(_ user: User) -> Bool {
if !searchText.isEmpty && [user.name, user.mobile, user.email].contains(where: { $0.contains(find: searchText) }) {
return true
}
if !qualification.isEmpty && user.qualification.contains(find: qualification) {
return true
}
...
return false
}
}
Then your big condition is reduced to:
if self.searchCriteria.matches(user) {
DispatchQueue.main.async {
self.phones.append(user.mobile)
self.names.append(user.name)
}
}
You cannot really avoid the conditions, but you can simplify them and organize them better, without duplicating code.
It seems you want to match all search conditions then I would change the matching method to:
func matchesUser(_ user: User) -> Bool {
if !searchText.isEmpty && ![user.name, user.mobile, user.email].contains(where: { $0.contains(find: searchText) }) {
return false
}
if !qualification.isEmpty && !user.qualification.contains(find: qualification) {
return false
}
...
return true
}
(note the double negation - contains condition negated and returning false).
You can use Guard statement to Early exit a scope .
A guard statement is used to transfer program control out of a scope
if one or more conditions aren’t met.
This will pass only if all fields are not empty.
guard self.searchtext != "", self.qualification != "", self.gender != "", self.year != "", self.major != "" else { return }
Update:
guard let searchText = self.searchtext, !searchText.isEmpty else { return }
// do something with `searchText`. Here the above conditions will be true.
OR you can use if-let
if let searchText = self.searchtext, !searchText.isEmpty {
// do with `searchText`(non-optional).
} else {
// conditions failed.
}
If you are trying to filter your array, you can simply use array.filter
let filteredUsers = usersArray.filter {
let shouldIAddThisElement = $0.name.contains(searchText) // Do your logic here
return shouldIAddThisElement
}
Swift includes guard statement. The lines after the guard statement will only be executed if the guard condition is true. Like an if/else statement, the else clause runs if the condition is false.
guard condition else {
// false: execute some code
}
// true: execute some code
For your case:
guard self.searchtext && self.qualification && self.gender && self.year && self.major else { return }
// execute some code
The return statement will exit the function or method.
More information can be found in the documentation on "Early Exit" Section:
https://docs.swift.org/swift-book/LanguageGuide/ControlFlow.html
I am trying to validate UITextField if it has greater than 3 characters it should return true else return false. I tried using the below code but it is not working. What am I doing wrong?
let validUserNameSignal = self.nameTextField.reactive.trigger(for: .valueChanged).observeValues {
value in
}.map { (value) in
String(describing: value).characters.count > 3 ? true:false
}
print("user name valid result is \(validUserNameSignal)")
Here's how the code should look.
let validUserNameSignal =
self.nameTextField
.reactive
.continuousTextValues
.skipNil()
.map { $0.characters.count > 3 }
validUserNameSignal.observeValues { value in
print("user name valid result is \(value)")
}
So when I press the button I check if both the UITextField and the UITextView have some value inside them, but if they are nil then perform this.
So I tried this, but it didn't work:
#IBOutlet weak var defTextView = UITextView
#IBAction func btnTapped(sender: UIButton) {
if let definitionName = defTextView.text {
print(definitionName)
} else {
print("nil")
}
}
Instead of receiving the word "nil" I got empty Strings bring printed
defTextView.text is empty String "" instead of nil.
Try where clause to check if it is empty:
#IBOutlet weak var defTextView = UITextView
#IBAction func btnTapped(sender: UIButton) {
if let definitionName = defTextView.text where !definitionName.isEmpty {
print(definitionName)
} else {
print("nil")
}
}
Your code should work, however, remember that an empty string is not nil. A UITextView's text is unlikely to be nil, so I would use an if statement to check if it is an empty string, as well as than if let unwrapping.
For example, use this instead:
if let defenitionName = defTextView.text where defTextView.text != nil {
print(definitionName)
} else {
print("none") //Not necessarily nil.
}
try following code
if defTextView.text != nil || !(defTextView.text == "") {
print(definitionName)
} else {
print("nil")
}
The question is actually meaningless because the text of a text view is never nil.
According the documentation the property text of UITextView is an implicit unwrapped optional - unlike UILabel whose text can be nil.
So it's guaranteed to be not nil when it's used and you need only to check for an empty string, optional binding or != nil is not needed.
#IBAction func btnTapped(sender: UIButton) {
let definitionName = defTextView.text
if definitionName.isEmpty {
print("definitionName is empty")
} else {
print("definitionName is not empty")
}
}
I'm trying to implement KSTokenView in my Swift 2 project. I have fixed all of the small errors in the conversion, but I have three instances of the same error that I can't figure out how to fix. The issue is with the advance method and I am getting a compile time error that says 'advance is unavailable: call the advancedBy(n)' method on the index. I've tried to look at another answer involving this method but after struggling for a while I can't figure it out.
The problem code is:
First instance is in the method below, I will mark it with a comment
private func _updateText() {
if (!_setupCompleted) {return}
_initPlaceholderLabel()
switch(_state) {
case .Opened:
text = KSTextEmpty
break
case .Closed:
if tokens.count == 0 {
text = KSTextEmpty
} else {
var title = KSTextEmpty
for token: KSToken in tokens {
title += "\(token.title)\(_separatorText!)"
}
if (title.characters.count > 0) {
//advance call made in the statement below
title = title.substringWithRange(Range<String.Index>(start: advance(title.startIndex, 0), end: advance(title.endIndex, -_separatorText!.characters.count)))
}
let width = KSUtils.widthOfString(title, font: font!)
if width + _leftViewRect().width > bounds.width {
text = "\(tokens.count) \(_descriptionText)"
} else {
text = title
}
}
break
}
_updatePlaceHolderVisibility()
}
Second and third instances are in this function called textField:shouldChangeCharactersInRange in the if statement if(string.isEmpty). I will also mark the if statement and the two advance method calls.
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
// If backspace is pressed
if (_tokenField.tokens.count > 0 && _tokenField.text == KSTextEmpty && string.isEmpty == true && shouldDeleteTokenOnBackspace) {
if (_lastToken() != nil) {
if (selectedToken() != nil) {
deleteSelectedToken()
} else {
_tokenField.selectToken(_lastToken()!)
}
}
return false
}
// Prevent removing KSEmptyString
if (string.isEmpty == true && _tokenField.text == KSTextEmpty) {
return false
}
var searchString: String
let olderText = _tokenField.text
// Check if character is removed at some index
// Remove character at that index
if (string.isEmpty) { //advance calls are made in this if statement
let first: String = olderText!.substringToIndex(advance(olderText!.startIndex, range.location)) as String // advance called here (1/2)
let second: String = olderText!.substringFromIndex(advance(olderText!.startIndex, range.location+1)) as String // advance called here (2/2)
searchString = first + second
} else { // new character added
if (tokenizingCharacters.contains(string) && olderText != KSTextEmpty && olderText!.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceCharacterSet()) != "") {
addTokenWithTitle(olderText!, tokenObject: nil)
return false
}
searchString = olderText!+string
}
// Allow all other characters
if (searchString.characters.count >= minimumCharactersToSearch && searchString != "\n") {
_lastSearchString = searchString
startSearchWithString(_lastSearchString)
}
_tokenField.scrollViewScrollToEnd()
return true
}
Edit: Figured it out. Take the first parameter and call advancedBy(n) on it. Then put the second parameter in the 'n' slot.
Example: let second: String = olderText!.substringFromIndex(olderText!.startIndex.advancedBy(range.location+1)) as String