Swift: Multiple conditions in IF statement - swift

I'm currently trying to validate a sign up page using an if statement with multiple conditions.
At the same time, I want to validate the email address with a correct email format.
Below is each field should essentially be checked.
#IBAction func createAccountButton(_ sender: Any) {
//check if textfields are empty and display alert
let providedEmailAddress = emailTextField.text
let isEmailAddressValid = isValidEmailAddress(emailAddressString: providedEmailAddress!)
if ( isEmailAddressValid || (firstNameTextField.text?.isEmpty)! || (lastNameTextField.text?.isEmpty)! || (usernameTextField.text?.isEmpty)! || (phoneNumberTextField.text?.isEmpty)! || (passwordTextField.text?.isEmpty)!) {
let alertViewController = SCLAlertView().showInfo("Failed to create account", subTitle: "One or more fields are empty, please check and try again.")
} else {
SVProgressHUD.show(withStatus: "Creating account...")
SVProgressHUD.dismiss(withDelay: 4)
}
}
The variable isEmailAddressValid is linked to this function I found on this site.
// Check if email address entered is of the valid format
func isValidEmailAddress(emailAddressString: String) -> Bool {
var returnValue = true
let emailRegEx = "[A-Z0-9a-z.-_]+#[A-Za-z0-9.-]+\\.[A-Za-z]{2,3}"
do {
let regex = try NSRegularExpression(pattern: emailRegEx)
let nsString = emailAddressString as NSString
let results = regex.matches(in: emailAddressString, range: NSRange(location: 0, length: nsString.length))
if results.count == 0
{
returnValue = false
}
} catch let error as NSError {
print("invalid regex: \(error.localizedDescription)")
returnValue = false
}
return returnValue
}
However, when testing I realised when I enter a value in each field except the emailTextfield.text and click to create an account, the else part of the statement is executed.
Moreover when all fields have a value, I would assume the else part of the statement WILL execute, but the 'failed to create an account' alert is displayed instead.
What am I doing wrong and what needs to be changed?

You should replace this part
if ( isEmailAddressValid |
with
if ( !isEmailAddressValid |

Related

Expected expression in conditional in let statement

I am getting this error on the after the let statement. Does anyone know why I am getting this error?
#objc private func didtaploginbutton() {
passwordTextField.resignFirstResponder()
usernameTextField.resignFirstResponder()
guard let usernameEmail = usernameTextField.text, !usernameEmail.isEmpty,
let password = passwordTextField.text, !password.isEmpty, password.count >= 8, else do {
return
}
}
Remove the do. It's usually only used when you want to handle an error with a Do-Catch block. You might also want to break the expression into multiple lines, so that it's easier to read.
Also, you have an extra comma (,) before the else. You need to remove this.
guard
let usernameEmail = usernameTextField.text,
!usernameEmail.isEmpty,
let password = passwordTextField.text,
!password.isEmpty,
password.count >= 8
else { return }

MultiLine String can be null, how to hide it

I am using multiline string as follows. there is a line where I display submittedPerson, either can be his Id or email, but also can be nil as well. I wonder how do you hide this line if it returns nil
var submittedPerson = ""
if let Id = User[index].Id {
submittedPerson = Id
} else if let email = User[index].email {
submittedPerson = email
}
let displayStr = """
\(department)
\"submittedBy" : \(submittedPerson)
\(submittedDate)
"""
I'll assume the hidden requirement here is that you still want to keep the multiline string literal readable, and not have code duplication :)
One way you could do this is to move one of the two line feed characters to when you assign to submittedPerson:
var submittedPerson: String? = ""
if let Id = User[index].Id {
submittedPerson = "\n\(Id)\n" // note the lines feeds
} else if let email = User[index].email {
submittedPerson = "\n\(email)\n"
}
let displayStr = """
\(department)
\(submittedPerson ?? "")
\(submittedDate)
"""

Why can't I search both first and last names using searchBar?

I have implemented code for searchBar and it works if I either search the first or the last name of a person. For example, if I want to search Kate Bell, the search works if I write "Kate", and it works if I write "Bell". But if I write "Kate B" the search result disappear.
Here's my code:
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
if searchText.count == 0 {
isFiltered = false
tableViewOutlet.reloadData()
}else {
isFiltered = true
searchInternalArray = contactsInternalArray.filter({ object -> Bool in
guard let text = searchBar.text else {return false}
return object.firstName.lowercased().contains(text.lowercased().trimmingCharacters(in: .whitespacesAndNewlines)) || object.lastName.lowercased().contains(text.lowercased().trimmingCharacters(in: .whitespacesAndNewlines))
})
searchArrayGroups = sectionsArrayGroups.filter({ object -> Bool in
guard let text = searchBar.text else {return false}
return object.firstName.lowercased().contains(text.lowercased().trimmingCharacters(in: .whitespacesAndNewlines)) || object.lastName.lowercased().contains(text.lowercased().trimmingCharacters(in: .whitespacesAndNewlines))
})
searchArraySAEs = sectionsArraySAEs.filter ({ object -> Bool in
guard let text = searchBar.text else {return false}
return object.firstName.lowercased().contains(text.lowercased().trimmingCharacters(in: .whitespacesAndNewlines)) || object.lastName.lowercased().contains(text.lowercased().trimmingCharacters(in: .whitespacesAndNewlines))
})
tableView.reloadData()
}
I fond this on SO How to search by both first name and last name
It's in objective-c and I have trouble implementing it into my own code. I tried something like this:
searchInternalArray = contactsInternalArray.filter({ object -> Bool in
guard let text = searchBar.text?.range(of: object.firstName + object.lastName) else {return false}
return object.firstName.lowercased().contains(text.lowercased().trimmingCharacters(in: .whitespacesAndNewlines)) || object.lastName.lowercased().contains(text.lowercased().trimmingCharacters(in: .whitespacesAndNewlines))
})
That's obviously not how it should be implemented though.
Edit
Didn't think it was relevant, but perhaps it is: I'm filtering multiple arrays since I have data coming from three different sources to the tableView. The whole searchBar code looks like above. It's updated.
Unless I'm missing something, there's a far simpler solution to this:
let name = firstname.lowercased() + " " + lastname.lowercased()
return name.contains(text.lowercased())
This works for any part of forename, surname, or the a part of the two with a space between. You should probably still trim leading/trailing spaces, and if you wanted to get rid of issues with variable spaces between the first and last names you could search/replace them double spaces with a single space.
Why not just create the full name from the two parts and search based on that.
let searchResults = contactsInternalArray.filter { object in
"\(object.firstName) \(object.lastName)".contains(searchText)
}
You can still lowercase them and trim if you want to.
I got idea from flanker's answer.
This works for me
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
self.searchBar.showsCancelButton = true
if searchText != ""{
self.isSearchActive = true
if self.object.count != 0{
filteredContacts = self. object.filter(){
//("\(($0 as FetchedContact).firstName.lowercased()) \(($0 as FetchedContact).object.lowercased()) \(($0 as FetchedContact).telephone.lowercased())")
let name = ("\(($0 as ContactModel).firstName.lowercased()) \(($0 as ContactModel).lastName.lowercased()) \(($0 as ContactModel).mobileNumber.lowercased())")
return name.contains(searchText.lowercased())
}
}
self.tblContactList.reloadData()
}
}
Which search on the base of FirsName, LastName and MobileNumber for Contacts.

How to get phone number URL from string with random text and a phone number inside

In my iOS application, I have a bunch of alertController messages containing text and one embedded phone number, and I wanted to offer the user the possibility from making the call from an alerControllerAction, and for that I need to be able to extract the phone number from the string dynamically, turn it into a phone number URL and let the old swift guy do its work, and so that's what I did after following about dozen of tuto around NSDataDetector, I came up with this function that for some reason always returns nil in my phoneNumberURL object. Could you guys check it out and tell me if something seems off ?
Here goes nothing :
private func showsHelpMessage()
{
let title = Bundle.main.localizedString(forKey: "account.help.popup.title",
value: "",
table: AFPConfig.sharedInstance.kLocalizableTable)
let message = Bundle.main.localizedString(forKey: "account.help.popup.message",
value: "",
table: AFPConfig.sharedInstance.kLocalizableTable)
var phoneNumber : String = ""
let detectorType: NSTextCheckingResult.CheckingType = [.phoneNumber]
do
{
let detector = try NSDataDetector(types: detectorType.rawValue)
let phoneNumberDetected = detector.firstMatch(in: message, options: [], range: NSRange(location: 0, length: message.utf16.count))
phoneNumber = (phoneNumberDetected?.phoneNumber)!
phoneNumber = phoneNumber.removeWhitespace() // added this because i noticed the NSURL kept crashing because of the whitespaces between numbers
}
catch
{
phoneNumber = "+33969390215"
}
if let phoneURL = NSURL(string: ("tel://" + phoneNumber))
{
let alertAccessibility = UIAlertController(title: title, message: message, preferredStyle: UIAlertController.Style.alert)
alertAccessibility.addAction(UIAlertAction(title: "Appeler ?", style: .destructive, handler: { (action) in
UIApplication.shared.open(phoneURL as URL, options: [:], completionHandler: nil)
}))
alertAccessibility.addAction(UIAlertAction(title: "Annuler", style: UIAlertAction.Style.cancel, handler: nil))
self.present(alertAccessibility, animated: true, completion: nil)
}
}
Thank you in advance, and cheers!
Addressing the problem of extracting numbers which can't be identified as definitely phone numbers (see comment on my other answer):
Rather than try to extract a number from the message and hope that it's a phone number and not a distance or a house number, introduce a placeholder (%d) into the localised string and insert the phone number into the message:
enum LocalPhoneNumbers {
case reception = 1000
case helpdesk = 4567
// etc.
}
private function showHelpMessage() {
// "Call the helpdesk on %d"
let format = Bundle.main.localizedString(forKey: "account.help.popup.message",
value: "",
table: AFPConfig.sharedInstance.kLocalizableTable)
let number = LocalPhoneNumbers.helpdesk.rawValue
let message = String(format: format, number)
let url = URL(string: "tel://\(number)")
// Code to show alert here...
}
Your approach seems ok, but I suspect maybe something about your input data is the real problem. Try experimenting with this in a playground:
import Foundation
enum PhoneNumberDetectionError: Error {
case nothingDetected
case noNumberFound
}
func extractPhoneURL(from string: String) throws -> URL? {
let detector = try! NSDataDetector(types: NSTextCheckingResult.CheckingType.phoneNumber.rawValue)
guard let detected = detector.firstMatch(in: string, options: [], range: NSRange(location: 0, length: string.utf16.count)) else {
throw PhoneNumberDetectionError.nothingDetected
}
guard let number = detected.phoneNumber else {
throw PhoneNumberDetectionError.noNumberFound
}
let noWhiteSpaces = number.filter { !$0.isWhitespace }
return URL(string: "tel://\(noWhiteSpaces)")
}
let strings = [
"This 555–692–7753 is a phone number",
"This 1 555 692 7753 is a phone number",
"This 123 is an incomplete phone number",
"This does not have a phone number",
"This +1 555 692 7753 is a phone number",
]
strings.forEach {
do {
guard let url = try extractPhoneURL(from: $0) else {
print("❌ '\($0)' failed to make URL")
return
}
print("✅ '\($0)' -> \(url.absoluteString)")
} catch {
print("❌ '\($0)' : \(error)")
}
}
If you get a ❌ for anything that you think should be valid, that's your problem.
Also, you've got some odd ( ) in a couple of places:
// Odd combination of optional unwrap and force-unwrap
phoneNumber = (phoneNumberDetected?.phoneNumber)!
// Concise equivalent
phoneNumber = phoneNumberDetected!.phoneNumber
And:
// The brackets around the string concatenation don't achieve anything
if let phoneURL = NSURL(string: ("tel://" + phoneNumber))
// Better
if let phoneURL = NSURL(string: "tel://" + phoneNumber)

Loops for Dictionary key items

How can I fix the code so the loop would ask the question for each of the items in the dictionary?
*Edited the code to make what I am trying to do more clear. I want the code to stop and wait for the input BEFORE moving on to the next item in the dictionary.
import Foundation
import UIKit
var squad = [1:"Arsh", 2:"Arbab", 3:"Ayush", 4:"KC", 5:"Jaski", 6:"Gunny", 7:"Harsh", 8:"Nagib", 9:"Rithin", 10:"Gursh", 11:"Sonny"]
let coming = "Yes"
let notComing = "No"
var attendees = [String]()
for ( key , person) in squad {
// key == 1
// key = counter
// repeat {
print("Are you attending \(person)?")
let response = readLine()
print(response as Any)
if response == coming {
attendees.append(person)
}
else if response == notComing {
print("Sorry to hear that")
}
else {
print("Invalid Input")
}
// } while key <= 11
// key += 1
}
print(attendees)
print(attendees.count)
To experiment keyboard input features like readLine(), create a project by choosing from Xcode menu :
File > New > Project > macOS > Command Line Tool
and run it in Xcode, the Debugging Console pane will work as a terminal. Here the code you're looking for :
import Foundation
var squad = [1:"Arsh", 2:"Arbab", 3:"Ayush", 4:"KC", 5:"Jaski", 6:"Gunny", 7:"Harsh", 8:"Nagib", 9:"Rithin", 10:"Gursh", 11:"Sonny"]
let coming = "yes"
let notComing = "no"
var attendees = [String]()
let array = squad.map {return ($0.key, $0.value)} // Turn the dictionary to an array of tuples
let sortedArray = array.sorted(by: {$0.0 < $1.0 }) // Sort the array by the first element of the tuple
// Ask the squad one by one
for ( _ , person) in sortedArray {
print("Are you attending \(person)?")
if let response = readLine() {
//remove white spaces and all letters lowercased so that "Yes", "yes" and "YES" would all be accepted
if response.lowercased().trimmingCharacters(in: .whitespaces) == coming {
attendees.append(person)
}
else if response.lowercased().trimmingCharacters(in: .whitespaces) == notComing {
print("Sorry to hear that")
}
else {
print("Invalid Input")
}
}
}
print("attendees : \(attendees)")
print("attendees.count = \(attendees.count)")