Email Validation is wrong as per Regex - ios

I am using email validation into my project which method is like below
//MARK: isValidEmailID
func isValidEmail(testStr:String) -> Bool {
print("validate emilId: \(testStr)")
let emailRegEx = "^(?:(?:(?:(?: )*(?:(?:(?:\\t| )*\\r\\n)?(?:\\t| )+))+(?: )*)|(?: )+)?(?:(?:(?:[-A-Za-z0-9!#$%&’*+/=?^_'{|}~]+(?:\\.[-A-Za-z0-9!#$%&’*+/=?^_'{|}~]+)*)|(?:\"(?:(?:(?:(?: )*(?:(?:[!#-Z^-~]|\\[|\\])|(?:\\\\(?:\\t|[ -~]))))+(?: )*)|(?: )+)\"))(?:#)(?:(?:(?:[A-Za-z0-9](?:[-A-Za-z0-9]{0,61}[A-Za-z0-9])?)(?:\\.[A-Za-z0-9](?:[-A-Za-z0-9]{0,61}[A-Za-z0-9])?)*)|(?:\\[(?:(?:(?:(?:(?:[0-9]|(?:[1-9][0-9])|(?:1[0-9][0-9])|(?:2[0-4][0-9])|(?:25[0-5]))\\.){3}(?:[0-9]|(?:[1-9][0-9])|(?:1[0-9][0-9])|(?:2[0-4][0-9])|(?:25[0-5]))))|(?:(?:(?: )*[!-Z^-~])*(?: )*)|(?:[Vv][0-9A-Fa-f]+\\.[-A-Za-z0-9._~!$&'()*+,;=:]+))\\])))(?:(?:(?:(?: )*(?:(?:(?:\\t| )*\\r\\n)?(?:\\t| )+))+(?: )*)|(?: )+)?$"
let emailTest = NSPredicate(format:"SELF MATCHES %#", emailRegEx)
let result = emailTest.evaluateWithObject(testStr)
return result
}
OR
func isValidEmailID(email: String) -> Bool {
let regExPattern: String = "[A-Z0-9a-z._%+-]+#[A-Za-z0-9.-]+\\.[A-Za-z]{2,}"
let emailValidator: NSPredicate = NSPredicate(format: "SELF MATCHES %#", regExPattern)
let isValid: Bool = emailValidator.evaluateWithObject(email)
return isValid
}
This both Regex works fine when I enter "modijecky#gmail.com" or any other wrong input but it will not work when I enter "modijecky#gmail.com.com".
So,I find out that "name#.com.com" is a valid email address and there are more sub-domains like this. So now I want user not to enter sub-domains. Is there any REGEX that validate email address within just one domain like "name#gmail.com" not with multiple domains or sub-domains.
I also try different Regex from google and implement it into project but same problem occurs.
Please help me with it.
Thank you

Don’t reinvent the wheel:
Not Reinventing the Wheel: Email Validation in Swift
Basically you can use NSDataDetector to do the heavy lifting and have everything consistent and updated to the way it works in macOS and iOS natively. Not only that but you also avoid regex headaches.
// Simplifying the example from the website a bit
import Foundation
func validate(_ text: String) -> Bool {
let types = NSTextCheckingResult.CheckingType.link.rawValue
guard
let dataDetector = try? NSDataDetector(types: types),
let match = dataDetector
.matches(in: text, options: [], range: NSRangeFromString(text))
.first,
let absoluteString = match.url?.absoluteString
else { return false }
return absoluteString == "mailto:\(text)"
}
validate("test#gmail.com") // -> true
validate(" test#gmail.com") // -> false
This will make sure that the entire text is a single, valid email address without any superfluous characters.

Function Call:
let result = isValidEmail(testStr: "test#test.com.op")
if (result)
{
print ("passed")
}
else{
print ("failed")
}
Function Definition:
func isValidEmail(testStr:String) -> Bool {
// print("validate calendar: \(testStr)")
var returnValue : Bool = false
let emailRegEx = "[A-Z0-9a-z._%+-]+#[A-Za-z0-9.-]+\\.[A-Za-z]{2,}"
let emailTest = NSPredicate(format:"SELF MATCHES %#", emailRegEx)
if (emailTest.evaluate(with: testStr))
{
let fullNameArr = testStr.components(separatedBy: "#")
let IdName = fullNameArr[0]
let domainName = fullNameArr[1]
var number = 0
let string = domainName
for character in domainName.characters {
if character == "."
{
number = number + 1
}
}
if number <= 1
{
returnValue = true
}
}
return returnValue
}
Result:

you should have this code to don't allow subdomain.
func isValidEmail(email:String) -> Bool {
if email.range(of: "#") == nil || email.range(of: ".") == nil{
return false
}
let accountName = email.substring(to: email.range(of: "#")!.lowerBound)
let domainName = email.substring(from: email.range(of: "#")!.upperBound)
let subDomain = domainName.substring(from: email.range(of: ".")!.lowerBound)
//filter for user name
let unWantedInUName = " ~!##$^&*()={}[]|;’:\"<>,?/`";
//filter for domain
let unWantedInDomain = " ~!##$%^&*()={}[]|;’:\"<>,+?/`";
//filter for subdomain
let unWantedInSub = " `~!##$%^&*()={}[]:\";’<>,?/1234567890";
//subdomain should not be less that 2 and not greater 6
if(!(subDomain.characters.count>=2 && subDomain.characters.count<=6)) {
return false;
}
if (accountName == "" || accountName.rangeOfCharacter(from: CharacterSet.init(charactersIn: unWantedInUName)) != nil || domainName == "" || domainName.rangeOfCharacter(from: CharacterSet.init(charactersIn: unWantedInDomain)) != nil || subDomain == "" || subDomain.rangeOfCharacter(from: CharacterSet.init(charactersIn: unWantedInSub)) != nil ) {
return false
}
return true
}

Related

Type of expression is ambiguous without more context in SWIFT string interpolation

I have React Native project. And I need in the native part of IOS to make string interpolation of formattedValue with a character, but I get the error "Type of expression is ambiguous without more context", and if I try the same code in the playground everything is working with out error. I'm not so experienced in SWIFT, can you tell me please why I get this error? And which the best way to make a string interpolation?
import Foundation
import SciChart.Protected.SCILabelProviderBase
class SCIAxisNumericLabelProvider: SCILabelProviderBase<ISCINumericAxis> {
var format: String?
var specialChar: String?
init(format: String?) {
let pattern = "[^0-9.]"
let regex = try! NSRegularExpression(pattern: pattern)
let formatValue = regex.stringByReplacingMatches(in: format ?? "", range: NSMakeRange(0, format?.count ?? 0), withTemplate: "")
self.format = formatValue
self.specialChar = format?.replacingOccurrences(of: "\(formatValue)", with: "")
super.init(axisType: ISCINumericAxis.self)
}
override func formatLabel(_ dataValue: ISCIComparable!) -> ISCIString! {
let dataValueToDouble = dataValue.toDouble()
let formattedValue = NSString(format: NSString(string: self.toFormat()), dataValueToDouble == -0 ? 0 : dataValueToDouble)
if let char = specialChar {
return "\(formattedValue) \(NSString(char))" // Here I get the error Type of expression is ambiguous without more context
}
return formattedValue
}
override func formatCursorLabel(_ dataValue: ISCIComparable!) -> ISCIString! {
return formatLabel(dataValue)
}
func toFormat() -> String {
if (self.format != nil) {
let a = self.format!.split(separator: ".")
if (a.count > 1) {
return "%0." + String(a[1].count) + "f"
}
}
return "%0.f"
}
func extractSpecialChar(value: String) {
}
}
In the end of the day I decided this error, I separated string interpolation and converting:
override func formatLabel(_ dataValue: ISCIComparable!) -> ISCIString! {
let formatterValue = self.toFormat();
let valueDouble = dataValue.toDouble()
let valueDoubleExcludedNegZero = valueDouble == -0 ? 0 : valueDouble
let precisionString = String(format: formatterValue, valueDoubleExcludedNegZero)
if let char = specialChar {
let resultString = precisionString + " " + char
return NSString(string: resultString)
}
return NSString(string: precisionString)
}

Multiple search in string - swift 4+

I have an simple problem with my code..
I wan't to make search like in facebook messenger - what do I mean?
I have multiple strings converted to one string without spaces like
"my string llama" -> "mystringllama"
I want to make searcher to search by two or more words like:
when I type "my ama" I want to search every record which have those characters..
Can someone helps?
My code:
func searchFor(text: String) {
if text == "" || text.count == 0 {
loadPricesFromDb()
}
else {
let realm = try! Realm()
self.items = []
let prices = realm.objects(Price.self)
let results = prices.filter({($0.MultipleSearchString?.lowercased().contains(text.rangeOfCharacter(from: text)))!
})
self.items.append(contentsOf: results)
self.tableView.reloadData()
}
}
And I just want to make it good. Now I don't know what I need to use as my filter to search it good.. :/
You may find a database command to get this kind of search. In swift, it's easy to construct such a predicate like the following if I understand your requirement right.
let multipleSearchString = "my stg l la ma"
let texts = ["mystringlladm1a", "mystr2ingllama", "mystri2ngllama", "mys3ringllama"]
let key = multipleSearchString.compactMap{ $0 == " " ? nil : String($0) + "*"}.joined()
let predicate = NSPredicate(format: "SELF like[c] %#", argumentArray: [key])
print (texts.filter{predicate.evaluate(with: $0)})
//["mystringlladm1a", "mystr2ingllama", "mystri2ngllama"]
In your added case, a computed property can be added to achieve your goal:
// extension Price{
// var lowerCasedMultipleSearchString: String{
// if let string = self.MultipleSearchString?.lowercased(){
// return string
// }
// return ""
// }
// }
// func searchTextInPrices(prices: Results<Price>, text: String) -> //Results<Price> {
// let key = text.compactMap{ $0 == " " ? nil : String($0) + "*"}.joined()
// let predicate = NSPredicate(format: "SELF like[c] %#", argumentArray: [key])
// return prices.filter{predicate.evaluate(with: $0.lowerCasedMultipleSearchString)}
// }
func searchFor(text: String) {
if text == "" || text.count == 0 {
loadPricesFromDb()
}
else {
let realm = try! Realm()
self.items = []
let prices = realm.objects(Price.self)
//The changed codes here.
let key = text.compactMap{ $0 == " " ? nil : String($0) + "*"}.joined()
let predicate = NSPredicate(format: "SELF like[c] %#", argumentArray: [key])
let results = prices.filter{predicate.evaluate(with: ($0.MultipleSearchString?.lowercased())!)}
//second edition:
var result = prices
var length : Int = 0
repeat{
let key = text[...(text.index(text.startIndex, offsetBy: length))].compactMap{ $0 == " " ? nil : String($0) + "*"}.joined()
let predicate = NSPredicate(format: "SELF like[c] %#", argumentArray: [key])
results = prices.filter{predicate.evaluate(with: ($0.MultipleSearchString?.lowercased())!)}
length += 1} while(length < text.count)
//Third edition:
let results = prices.filter{ text.reduce((($0.MultipleSearchString?.lowercased())!, true)){
if !$0.1 {return ("",false)}
else if let index = $0.0.index(of: $1) {return (String($0.0[index...]),true)}
return ("",false)
}.1}
self.items.append(contentsOf: results)
self.tableView.reloadData()
}
}

Check the valid phone number

I have one text field for enter the phone number and user have to press OK button.
Then I write some function to check whether entered number is valid number or 10 digit number. And I don't want to add country code. That I have separately.
But when I press OK button its give me uialert - wrong number for all number including my own number. I don't know any code I missed?
func validate(value: String) -> Bool {
let PHONE_REGEX = "^\\d{3}-\\d{3}-\\d{4}$"
var phoneTest = NSPredicate(format: "SELF MATCHES %#", PHONE_REGEX)
var result = phoneTest.evaluateWithObject(value)
return result
}
#IBAction func confirmAction(sender: AnyObject) {
if validate(phoneNumber.text!)
{
print("Validate EmailID")
let phone = countryCode.text! + phoneNumber.text!
UserNetworkInterface().generateSms(phone, onCompletion: nil)
performSegueWithIdentifier("ConfirmSmsCode", sender: self)
}
else
{
print("invalide EmailID")
let alert = UIAlertView()
alert.title = "Message"
alert.message = "Enter Valid Contact Number"
alert.addButtonWithTitle("Ok")
alert.delegate = self
alert.show()
}
}
Updated :
#IBAction func confirmAction(sender: AnyObject) {
if let phoneNumberValidator = phoneNumber.isPhoneNumber
{
print("Validate EmailID")
let phone = countryCode.text! + phoneNumber.text!
UserNetworkInterface().generateSms(phone, onCompletion: nil)
performSegueWithIdentifier("ConfirmSmsCode", sender: self)
}
else
{
print("invalide EmailID")
let alert = UIAlertView()
alert.title = "Message"
alert.message = "Enter Valid Contact Number"
alert.addButtonWithTitle("Ok")
alert.delegate = self
alert.show()
phoneNumber.text = ""
}
// Number valid
}
Try this.
Make an extension to String.
Swift 4
extension String {
var isPhoneNumber: Bool {
do {
let detector = try NSDataDetector(types: NSTextCheckingResult.CheckingType.phoneNumber.rawValue)
let matches = detector.matches(in: self, options: [], range: NSRange(location: 0, length: self.count))
if let res = matches.first {
return res.resultType == .phoneNumber && res.range.location == 0 && res.range.length == self.count
} else {
return false
}
} catch {
return false
}
}
}
Older Swift Versions
extension String {
var isPhoneNumber: Bool {
do {
let detector = try NSDataDetector(types: NSTextCheckingType.PhoneNumber.rawValue)
let matches = detector.matchesInString(self, options: [], range: NSMakeRange(0, self.characters.count))
if let res = matches.first {
return res.resultType == .PhoneNumber && res.range.location == 0 && res.range.length == self.characters.count
} else {
return false
}
} catch {
return false
}
}
}
Usage:
override func viewWillAppear(animated: Bool) {
//Sample check
let phoneString = "8888888888"
let phoneNumberValidator = phoneString.isPhoneNumber
print(phoneNumberValidator)
}
Swift 3
For those of you who would like the phone number to have a minimum of 10 characters use the below code (Amended #Alvin George code)
extension String {
var isPhoneNumber: Bool {
do {
let detector = try NSDataDetector(types: NSTextCheckingResult.CheckingType.phoneNumber.rawValue)
let matches = detector.matches(in: self, options: [], range: NSMakeRange(0, self.characters.count))
if let res = matches.first {
return res.resultType == .phoneNumber && res.range.location == 0 && res.range.length == self.characters.count && self.characters.count == 10
} else {
return false
}
} catch {
return false
}
}
}
Usage
ooverride func viewDidLoad() {
super.viewDidLoad()
//Example
let phoneNumberString = "8500969696"
let phoneNumberValidation = phoneNumberString.isPhoneNumber
print(phoneNumberValidation)
// Prints: true
}
Try this for Indian 10 digit mobile number validation
var isValidMobileNo: Bool {
let PHONE_REGEX = "^[7-9][0-9]{9}$";
let phoneTest = NSPredicate(format: "SELF MATCHES %#", PHONE_REGEX)
let result = phoneTest.evaluate(with: self)
return result
}
Amended #Alvin George code for Swift 4 that also accepts only 10 digit phone numbers:
extension String {
var isPhoneNumber: Bool {
do {
let detector = try NSDataDetector(types: NSTextCheckingResult.CheckingType.phoneNumber.rawValue)
let matches = detector.matches(in: self, options: [], range: NSMakeRange(0, self.count))
if let res = matches.first {
return res.resultType == .phoneNumber && res.range.location == 0 && res.range.length == self.count && self.count == 10
} else {
return false
}
} catch {
return false
}
}
}
Easy validation for Indian 10 digit mobile number
func isValidPhone(testStr:String) -> Bool {
let phoneRegEx = "^[6-9]\\d{9}$"
var phoneNumber = NSPredicate(format:"SELF MATCHES %#", phoneRegEx)
return phoneNumber.evaluate(with: testStr)
}
Modifying #A.G's answer to update to Swift 5:
extension String {
func isValidPhone() -> Bool {
do {
let detector = try NSDataDetector(types: NSTextCheckingResult.CheckingType.phoneNumber.rawValue)
let matches = detector.matches(in: self, options: [], range: NSMakeRange(0, self.count))
if let res = matches.first {
return res.resultType == .phoneNumber && res.range.location == 0 && res.range.length == self.count
} else {
return false
}
} catch {
return false
}
}
}

Ignore a letter in swift which starts with a Lower Case

Here's what I am trying to do :
let courseName = "Bachelor of Tourism Administration(B.T.A)".condensedWhitespace
let upperCaseCourseName = courseName.uppercaseString
let extrctCourseName = upperCaseCourseName.componentsSeparatedByString(" ").reduce("") { $0.0 + String($0.1.characters.first!) }
let upperCasecourseFirstCharcters = extrctCourseName
print(upperCasecourseFirstCharcters) // output : "BOTA" but i want "BTA"
as you see that my outPut of "Bachelor of Tourism Administration(B.T.A)" is BOTA but the desired output is BTA because word of is starting from a lowerCase and i want to ignore that word in my this method , how am gonna do that any idea ?
let courseName = "Bachelor of Tourism Administration(B.T.A)" //.condensedWhitespace
var newString = ""
let array : NSArray = courseName.componentsSeparatedByString(" ")
for chr in array {
let str = chr as! NSString
if str.lowercaseString != str{
if newString.characters.count > 0{
newString = newString.stringByAppendingString(" "+(str as String))
continue
}
newString = newString.stringByAppendingString((str as String))
}
}
let upperCaseCourseName = newString.uppercaseString
let extrctCourseName = upperCaseCourseName.componentsSeparatedByString(" ").reduce("") { $0.0 + String($0.1.characters.first!) }
let upperCasecourseFirstCharcters = extrctCourseName
print(upperCasecourseFirstCharcters)
//This will defiantly meet to your problem/. Let me know if it works for u or not
You can paste this into a playground:
extension String {
func array() -> [String] {
return self.componentsSeparatedByString(" ")
}
func abbreviate() -> String {
var output = ""
let array = self.array()
for word in array {
let index = word.startIndex.advancedBy(0)
let str = String(word[index])
if str.lowercaseString != str {
output += str
}
}
return output
}
}
let courseName = "Bachelor of Tourism Administration(B.T.A)".abbreviate()
print(courseName) // prints BTA
A clean approach would be:
extension Character
{
public func isUpper() -> Bool
{
let characterString = String(self)
return (characterString == characterString.uppercaseString) && (characterString != characterString.lowercaseString)
}
}
let courseName = "Bachelor of Tourism Administration(B.T.A)"
let upperCaseCourseName = courseName
let extrctCourseName = upperCaseCourseName.componentsSeparatedByString(" ").reduce("") {
if($0.1.characters.first!.isUpper()) {
return $0.0 + String($0.1.characters.first!)
}else {
return $0.0
}
}

NSPredicate filter array using all characters before delimiter

I am currently filtering using
self.searchArray.removeAll(keepCapacity: false)
let searchPredicate = NSPredicate(format: "SELF CONTAINS[c] %#", searchController.searchBar.text!)
let array = (Array(exampleArray) as NSArray).filteredArrayUsingPredicate(searchPredicate)
self.searchArray = array as! [String]
It is filtering the whole string, However, I only want to filter using all characters that exist before my delimiter. For example:
each array value has a delimiter which is "$%^"
example array contains [abc$%^12], [efg$%^32], [tyh$%^77]
I only want the filtering to include on all characters before $%^ which would be abc, efg, and tyh
You can do this without using NSPredicate. Stub:
let searchBarText = "h"
let exampleArray = ["abc$%^12", "efg$%^32", "tyh$%^77"]
let searchResults = exampleArray.filter {
let components = $0.componentsSeparatedByString("$%^")
return components[0].containsString(searchBarText)
}
'pure' Swift solution :-) (I don't like any help of Foundation)
let arr = ["abc$%^12", "efg$%^32", "tyh$%^77", "nodelemiter"]
let delimiter = "$%^"
func splitByDelimeter(let str: String, delimiter: String)->(String,String) {
var str1 = ""
var str2 = str
while !str2.hasPrefix(delimiter) && str2.characters.count > 0 {
str1.append(str2.removeAtIndex(str2.startIndex))
}
if (str1 == str) { return (str1, "") }
let r = Range(start: str2.startIndex, end: str2.startIndex.advancedBy(delimiter.characters.count))
str2.removeRange(r)
return (str1, str2)
}
let res = arr.map { splitByDelimeter($0, delimiter: delimiter).0 }
print(res) // ["abc", "efg", "tyh", "nodelemiter"]
let res2 = arr.map { splitByDelimeter($0, delimiter: delimiter) }
print(res2) // [("abc", "12"), ("efg", "32"), ("tyh", "77"), ("nodelemiter", "")]
it should work, even there is only delimiter, or more than one delimiter in your string. First occurrence of delimiter will split the string to two parts.
UPDATE
with help of String extension you can do it as ...
let arr = ["abc$%^12", "efg$%^32", "tyh$%^77", "nodelemiter", "$%^jhdk$%^jdhjahsd", "22lemar$%^fralemdo"]
extension String {
func splitBy(delimiter: String)->(String,String) {
var str1 = ""
var str2 = self
while !str2.hasPrefix(delimiter) && str2.characters.count > 0 {
str1.append(str2.removeAtIndex(str2.startIndex))
}
if (str1 == self) { return (str1, "") }
let r = Range(start: str2.startIndex, end: str2.startIndex.advancedBy(delimiter.characters.count))
str2.removeRange(r)
return (str1, str2)
}
func contains(string: String)->Bool {
guard !self.isEmpty else {
return false
}
var s = self.characters.map{ $0 }
let c = string.characters.map{ $0 }
repeat {
if s.startsWith(c){
return true
} else {
s.removeFirst()
}
} while s.count > c.count - 1
return false
}
}
let filtered = arr.map{ $0.splitBy("$%^").0 }.filter { $0.contains("lem") }
print(filtered) // ["nodelemiter", "22lemar"]
or you can use hasPrefix to narrow search by typing more chars to you search box ... etc.
let arr2 = ["abc$%^12", "abefg$%^32", "tyhab$%^77"]
let filtered2 = arr2.map{ $0.splitBy("$%^").0 }.filter { $0.hasPrefix("ab") }
print(filtered2) // ["abc", "abefg"]

Resources