How to validate an e-mail address in swift? - ios

Does anyone know how to validate an e-mail address in Swift? I found this code:
- (BOOL) validEmail:(NSString*) emailString {
if([emailString length]==0){
return NO;
}
NSString *regExPattern = #"[A-Z0-9a-z._%+-]+#[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}";
NSRegularExpression *regEx = [[NSRegularExpression alloc] initWithPattern:regExPattern options:NSRegularExpressionCaseInsensitive error:nil];
NSUInteger regExMatches = [regEx numberOfMatchesInString:emailString options:0 range:NSMakeRange(0, [emailString length])];
NSLog(#"%i", regExMatches);
if (regExMatches == 0) {
return NO;
} else {
return YES;
}
}
but I can't translate it to Swift.

I would use NSPredicate:
func isValidEmail(_ email: String) -> Bool {
let emailRegEx = "[A-Z0-9a-z._%+-]+#[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
let emailPred = NSPredicate(format:"SELF MATCHES %#", emailRegEx)
return emailPred.evaluate(with: email)
}
for versions of Swift earlier than 3.0:
func isValidEmail(email: String) -> Bool {
let emailRegEx = "[A-Z0-9a-z._%+-]+#[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
let emailPred = NSPredicate(format:"SELF MATCHES %#", emailRegEx)
return emailPred.evaluate(with: email)
}
for versions of Swift earlier than 1.2:
func isValidEmail(email: String) -> Bool {
let emailRegEx = "[A-Z0-9a-z._%+-]+#[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
if let emailPred = NSPredicate(format:"SELF MATCHES %#", emailRegEx) {
return emailPred.evaluateWithObject(email)
}
return false
}

As a String class extension
SWIFT 4
extension String {
func isValidEmail() -> Bool {
// here, `try!` will always succeed because the pattern is valid
let regex = try! NSRegularExpression(pattern: "^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+#[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])?)*$", options: .caseInsensitive)
return regex.firstMatch(in: self, options: [], range: NSRange(location: 0, length: count)) != nil
}
}
Usage
if "rdfsdsfsdfsd".isValidEmail() {
}

Editing, updated for Swift 3:
func validateEmail(enteredEmail:String) -> Bool {
let emailFormat = "[A-Z0-9a-z._%+-]+#[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
let emailPredicate = NSPredicate(format:"SELF MATCHES %#", emailFormat)
return emailPredicate.evaluate(with: enteredEmail)
}
Original answer for Swift 2:
func validateEmail(enteredEmail:String) -> Bool {
let emailFormat = "[A-Z0-9a-z._%+-]+#[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
let emailPredicate = NSPredicate(format:"SELF MATCHES %#", emailFormat)
return emailPredicate.evaluateWithObject(enteredEmail)
}
It's working fine.

If you are looking for a clean and simple solution to do this, you should take a look at https://github.com/nsagora/validation-components.
It contains an email validation predicate which is easy integrate in your code:
let email = "test#example.com"
let rule = EmailValidationPredicate()
let isValidEmail = rule.evaluate(with: email)
Behind the hood it uses the RFC 5322 reg ex (http://emailregex.com):
let regex = "(?:[\\p{L}0-9!#$%\\&'*+/=?\\^_`{|}~-]+(?:\\.[\\p{L}0-9!#$%\\&'*+/=?\\^_`{|}" +
"~-]+)*|\"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\" +
"x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*\")#(?:(?:[\\p{L}0-9](?:[a-" +
"z0-9-]*[\\p{L}0-9])?\\.)+[\\p{L}0-9](?:[\\p{L}0-9-]*[\\p{L}0-9])?|\\[(?:(?:25[0-5" +
"]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-" +
"9][0-9]?|[\\p{L}0-9-]*[\\p{L}0-9]:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21" +
"-\\x5a\\x53-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)\\])"

(Preamble. Be aware that in some cases, you can now use this solution built-in to iOS: https://multithreaded.stitchfix.com/blog/2016/11/02/email-validation-swift/ )
The only solution:
1 - it avoids the horrific regex mistakes often seen in example code
2 - it does NOT allow ridiculous emails such as "x#x"
(If for some reason you need a solution that allows nonsense strings such as 'x#x', use another solution.)
3 - the code is extremely understandable
4 - it is KISS, reliable, and tested to destruction on commercial apps with enormous numbers of users
5 - the predicate is a global, as Apple says it must be
let __firstpart = "[A-Z0-9a-z]([A-Z0-9a-z._%+-]{0,30}[A-Z0-9a-z])?"
let __serverpart = "([A-Z0-9a-z]([A-Z0-9a-z-]{0,30}[A-Z0-9a-z])?\\.){1,5}"
let __emailRegex = __firstpart + "#" + __serverpart + "[A-Za-z]{2,8}"
let __emailPredicate = NSPredicate(format: "SELF MATCHES %#", __emailRegex)
extension String {
func isEmail() -> Bool {
return __emailPredicate.evaluate(with: self)
}
}
extension UITextField {
func isEmail() -> Bool {
return self.text?.isEmail() ?? false
}
}
It's that easy.
Explanation for anyone new to regex:
In this description, "OC" means ordinary character - a letter or a digit.
__firstpart ... has to start and end with an OC. For the characters in the middle you can have certain characters such as underscore, but the start and end have to be an OC. (However, it's ok to have only one OC and that's it, for example: j#blah.com)
__serverpart ... You have sections like "blah." which repeat. (Example, mail.city.fcu.edu.) The sections have to start and end with an OC, but in the middle you can also have a dash "-". It's OK to have a section which is just one OC. (Example, w.campus.edu) You can have up to five sections, you have to have one. Finally the TLD (such as .com) is strictly 2 to 8 in size . (Obviously, just change the "8" as preferred by your support department.)
IMPORTANT !
You MUST keep the predicate as a global, do not build it every time.
Note that this is the first thing Apple mentions about the whole issue in the docs.
Suggestions which do not cache the predicate are non-starters.
Non-english alphabets
Naturally, if you deal with non-english alphabets, adjust appropriately.

Simplest way in Swift 5
extension String {
var isValidEmail: Bool {
NSPredicate(format: "SELF MATCHES %#", "[A-Z0-9a-z._%+-]+#[A-Za-z0-9.-]+\\.[A-Za-z]{2,}").evaluate(with: self)
}
}
Example
"kenmueller0#gmail.com".isValidEmail
returns...
true

Here is a fuse of the two most up-voted answer with the correct regex: a String extension using predicate so you can call string.isEmail
extension String {
var isEmail: Bool {
let emailRegEx = "[A-Z0-9a-z._%+-]+#[A-Za-z0-9.-]+\\.[A-Za-z]{2,20}"
let emailTest = NSPredicate(format:"SELF MATCHES %#", emailRegEx)
return emailTest.evaluateWithObject(self)
}
}

I would suggest using it as an extension of String:
extension String {
public var isEmail: Bool {
let dataDetector = try? NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue)
let firstMatch = dataDetector?.firstMatch(in: self, options: NSRegularExpression.MatchingOptions.reportCompletion, range: NSRange(location: 0, length: length))
return (firstMatch?.range.location != NSNotFound && firstMatch?.url?.scheme == "mailto")
}
public var length: Int {
return self.characters.count
}
}
And to use it:
if "hodor#gameofthrones.com".isEmail { // true
print("Hold the Door")
}

This is the updated version for Swift 2.0 - 2.2
var isEmail: Bool {
do {
let regex = try NSRegularExpression(pattern: "^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+#[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])?)*$", options: .CaseInsensitive)
return regex.firstMatchInString(self, options: NSMatchingOptions(rawValue: 0), range: NSMakeRange(0, self.characters.count)) != nil
} catch {
return false
}
}

There are a lot of right answers here, but many of the "regex" are incomplete and it can happen that an email like: "name#domain" results a valid email, but it is not. Here the complete solution:
extension String {
var isEmailValid: Bool {
do {
let regex = try NSRegularExpression(pattern: "(?:[a-z0-9!#$%\\&'*+/=?\\^_`{|}~-]+(?:\\.[a-z0-9!#$%\\&'*+/=?\\^_`{|}~-]+)*|\"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*\")#(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21-\\x5a\\x53-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)\\])", options: .CaseInsensitive)
return regex.firstMatchInString(self, options: NSMatchingOptions(rawValue: 0), range: NSMakeRange(0, self.characters.count)) != nil
} catch {
return false
}
}
}

Here is a method based on rangeOfString:
class func isValidEmail(testStr:String) -> Bool {
let emailRegEx = "[A-Z0-9a-z._%+-]+#[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
let range = testStr.rangeOfString(emailRegEx, options:.RegularExpressionSearch)
return range != nil
}
Note: updated TLD length.
Here is the definitive RegEx for email as per RFC 5322, note that this is best not used because it only checks the basic syntax of email addresses and does not check is the top level domain exists.
(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*
| "(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]
| \\[\x01-\x09\x0b\x0c\x0e-\x7f])*")
# (?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?
| \[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}
(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:
(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]
| \\[\x01-\x09\x0b\x0c\x0e-\x7f])+)
\])
See Regular-Expressions.info for more complete information on email RegExs.
Note that no escaping as required by a language such as Objective-C or Swift.

I prefer use an extension for that. Besides, this url http://emailregex.com can help you to test if regex is correct. In fact, the site offers differents implementations for some programming languages. I share my implementation for Swift 3.
extension String {
func validateEmail() -> Bool {
let emailRegex = "[A-Z0-9a-z._%+-]+#[A-Za-z0-9.-]+\\.[A-Za-z]{2,6}"
return NSPredicate(format: "SELF MATCHES %#", emailRegex).evaluate(with: self)
}
}

This a new version for "THE REASONABLE SOLUTION" by #Fattie, tested on Swift 4.1 in a new file called String+Email.swift:
import Foundation
extension String {
private static let __firstpart = "[A-Z0-9a-z]([A-Z0-9a-z._%+-]{0,30}[A-Z0-9a-z])?"
private static let __serverpart = "([A-Z0-9a-z]([A-Z0-9a-z-]{0,30}[A-Z0-9a-z])?\\.){1,5}"
private static let __emailRegex = __firstpart + "#" + __serverpart + "[A-Za-z]{2,6}"
public var isEmail: Bool {
let predicate = NSPredicate(format: "SELF MATCHES %#", type(of:self).__emailRegex)
return predicate.evaluate(with: self)
}
}
So its usage is simple:
let str = "mail#domain.com"
if str.isEmail {
print("\(str) is a valid e-mail address")
} else {
print("\(str) is not a valid e-mail address")
}
I simply don't like to add a func to the String objects, as being an e-mail address is inherent to them (or not). So a Bool property would fit better than a func, from my understanding.

For swift 2.1: this works correctly with email foo#bar
extension String {
func isValidEmail() -> Bool {
do {
let regex = try NSRegularExpression(pattern: "[A-Z0-9a-z._%+-]+#[A-Za-z0-9.-]+\\.[A-Za-z]{2,6}", options: .CaseInsensitive)
return regex.firstMatchInString(self, options: NSMatchingOptions(rawValue: 0), range: NSMakeRange(0, self.characters.count)) != nil
} catch {
return false
}
}
}

Use of Swift 4.2
extension String {
func isValidEmail() -> Bool {
let regex = try? NSRegularExpression(pattern: "^(((([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+(\\.([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+)*)|((\\x22)((((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(([\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(\\([\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}]))))*(((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(\\x22)))#((([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])([a-zA-Z]|\\d|-|\\.|_|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.)+(([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])([a-zA-Z]|\\d|-|_|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.?$", options: .caseInsensitive)
return regex?.firstMatch(in: self, options: [], range: NSMakeRange(0, self.count)) != nil
}
func isValidName() -> Bool{
let regex = try? NSRegularExpression(pattern: "^[\\p{L}\\.]{2,30}(?: [\\p{L}\\.]{2,30}){0,2}$", options: .caseInsensitive)
return regex?.firstMatch(in: self, options: [], range: NSMakeRange(0, self.count)) != nil
} }
And used
if (textField.text?.isValidEmail())!
{
// bla bla
}
else
{
}

Make a simple test for a # and . and send a confirmation email.
Consider this:
Half of the world uses non-ASCII characters.
Regexes are slow and complex. Btw check at least for char/letter/Unicode range, not az.
You can’t afford full validation because RFC rules and corresponding regex are too complex.
I’m using this basic check:
// similar to https://softwareengineering.stackexchange.com/a/78372/22077
import Foundation
/**
Checks that
- length is 254 or less (see https://stackoverflow.com/a/574698/412916)
- there is a # which is not the first character
- there is a . after the #
- there are at least 4 characters after the #
*/
func isValidEmail(email: String) -> Bool {
guard email.count <= 254 else {
return false
}
let pos = email.lastIndex(of: "#") ?? email.endIndex
return (pos != email.startIndex)
&& ((email.lastIndex(of: ".") ?? email.startIndex) > pos)
&& (email[pos...].count > 4)
}
print(isValidEmail(email: "アシッシュ#ビジネス.コム")) // true
Note that
It is considerably faster than regex and NSDataDetector.
It correctly reports the following as valid:
Håkan.Söderström#malmö.se"
punnycode#XN--0ZWM56D.XN--HGBK6AJ7F53BBA"
试#例子.测试.مثال.آزمایشی"
foo.bar+something#blah.com"
m#foo.co.uk
It incorrectly reports the following as invalid –because they are actually valid but likely the product of a user error:
a # b
a#b
Related:
How far should one take e-mail address validation?
online email checker: https://isemail.info/

#JeffersonBe's answer is close, but returns true if the string is "something containing someone#something.com a valid email" which is not what we want. The following is an extension on String that works well (and allows testing for valid phoneNumber and other data detectors to boot.
/// Helper for various data detector matches.
/// Returns `true` iff the `String` matches the data detector type for the complete string.
func matchesDataDetector(type: NSTextCheckingResult.CheckingType, scheme: String? = nil) -> Bool {
let dataDetector = try? NSDataDetector(types: type.rawValue)
guard let firstMatch = dataDetector?.firstMatch(in: self, options: NSRegularExpression.MatchingOptions.reportCompletion, range: NSRange(location: 0, length: length)) else {
return false
}
return firstMatch.range.location != NSNotFound
// make sure the entire string is an email, not just contains an email
&& firstMatch.range.location == 0
&& firstMatch.range.length == length
// make sure the link type matches if link scheme
&& (type != .link || scheme == nil || firstMatch.url?.scheme == scheme)
}
/// `true` iff the `String` is an email address in the proper form.
var isEmail: Bool {
return matchesDataDetector(type: .link, scheme: "mailto")
}
/// `true` iff the `String` is a phone number in the proper form.
var isPhoneNumber: Bool {
return matchesDataDetector(type: .phoneNumber)
}
/// number of characters in the `String` (required for above).
var length: Int {
return self.characters.count
}

Create simple extension:
extension NSRegularExpression {
convenience init(pattern: String) {
try! self.init(pattern: pattern, options: [])
}
}
extension String {
var isValidEmail: Bool {
return isMatching(expression: NSRegularExpression(pattern: "^[A-Z0-9a-z\\._%+-]+#([A-Za-z0-9-]+\\.)+[A-Za-z]{2,4}$"))
}
//MARK: - Private
private func isMatching(expression: NSRegularExpression) -> Bool {
return expression.numberOfMatches(in: self, range: NSRange(location: 0, length: characters.count)) > 0
}
}
Example:
"b#bb.pl".isValidEmail //true
"b#bb".isValidEmail //false
You can extend following extension to anything you need: isValidPhoneNumber, isValidPassword etc...

I made a library designed for input validations and one of the "modules" allows you to easily validate a bunch of stuff...
For example to validate an email:
let emailTrial = Trial.Email
let trial = emailTrial.trial()
if(trial(evidence: "test#test.com")) {
//email is valid
}
SwiftCop is the library... hope it help!

Updated answer #Arsonik answer to Swift 2.2, using less verbose code than other offered solutions:
extension String {
func isValidEmail() -> Bool {
let regex = try? NSRegularExpression(pattern: "^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+#[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])?)*$", options: .CaseInsensitive)
return regex?.firstMatchInString(self, options: [], range: NSMakeRange(0, self.characters.count)) != nil
}
}

My only addition to the list of responses would be that for Linux, NSRegularExpression does not exist, it's actually RegularExpression
func isEmail() -> Bool {
let patternNormal = "[A-Z0-9a-z._%+-]+#[A-Za-z0-9.-]+\\.[A-Za-z]{2,6}"
#if os(Linux)
let regex = try? RegularExpression(pattern: patternNormal, options: .caseInsensitive)
#else
let regex = try? NSRegularExpression(pattern: patternNormal, options: .caseInsensitive)
#endif
return regex?.firstMatch(in: self, options: [], range: NSMakeRange(0, self.characters.count)) != nil
This compiles successfully on both macOS & Ubuntu.

Here is an extension in Swift 3
extension String {
func isValidEmail() -> Bool {
let emailRegex = "[A-Z0-9a-z._%+-]+#[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
return NSPredicate(format: "SELF MATCHES %#", emailRegex).evaluate(with: self)
}
}
Just use it like this:
if yourEmailString.isValidEmail() {
//code for valid email address
} else {
//code for not valid email address
}

Best solution with best result for
Swift 4.x
extension String {
func validateAsEmail() -> Bool {
let emailRegEx = "(?:[a-zA-Z0-9!#$%\\&‘*+/=?\\^_`{|}~-]+(?:\\.[a-zA-Z0-9!#$%\\&'*+/=?\\^_`{|}" +
"~-]+)*|\"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\" +
"x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*\")#(?:(?:[a-z0-9](?:[a-" +
"z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\\[(?:(?:25[0-5" +
"]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-" +
"9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21" +
"-\\x5a\\x53-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)\\])"
let emailTest = NSPredicate(format:"SELF MATCHES[c] %#", emailRegEx)
return emailTest.evaluate(with: self)
}
}

I improved #Azik answer. I allow more special characters which are allowed by guidelines, as well as return a few extra edge cases as invalid.
The group think going on here to only allow ._%+- in the local part is not correct per guidelines. See #Anton Gogolev answer on this question or see below:
The local-part of the email address may use any of these ASCII
characters:
uppercase and lowercase Latin letters A to Z and a to z;
digits 0 to 9;
special characters !#$%&'*+-/=?^_`{|}~;
dot ., provided that it is not the first or last character unless quoted, and provided also that it does not appear consecutively unless quoted (e.g.
John..Doe#example.com is not allowed but "John..Doe"#example.com is
allowed);
space and "(),:;<>#[\] characters are allowed with
restrictions (they are only allowed inside a quoted string, as
described in the paragraph below, and in addition, a backslash or
double-quote must be preceded by a backslash);
comments are allowed
with parentheses at either end of the local-part; e.g.
john.smith(comment)#example.com and (comment)john.smith#example.com
are both equivalent to john.smith#example.com;
The code I use will not allow restricted out of place special characters, but will allow many more options than the majority of answers here. I would prefer more relaxed validation to error on the side of caution.
if enteredText.contains("..") || enteredText.contains("##")
|| enteredText.hasPrefix(".") || enteredText.hasSuffix(".con"){
return false
}
let emailFormat = "[A-Z0-9a-z.!#$%&'*+-/=?^_`{|}~]+#[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
let emailPredicate = NSPredicate(format:"SELF MATCHES %#", emailFormat)
return emailPredicate.evaluate(with: enteredText)

In Swift 4.2 and Xcode 10.1
//Email validation
func isValidEmail(email: String) -> Bool {
let emailRegex = "[A-Z0-9a-z._%+-]+#[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}"
var valid = NSPredicate(format: "SELF MATCHES %#", emailRegex).evaluate(with: email)
if valid {
valid = !email.contains("Invalid email id")
}
return valid
}
//Use like this....
let emailTrimmedString = emailTF.text?.trimmingCharacters(in: .whitespaces)
if isValidEmail(email: emailTrimmedString!) == false {
SharedClass.sharedInstance.alert(view: self, title: "", message: "Please enter valid email")
}
If you want to use SharedClass.
//This is SharedClass
import UIKit
class SharedClass: NSObject {
static let sharedInstance = SharedClass()
//Email validation
func isValidEmail(email: String) -> Bool {
let emailRegex = "[A-Z0-9a-z._%+-]+#[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}"
var valid = NSPredicate(format: "SELF MATCHES %#", emailRegex).evaluate(with: email)
if valid {
valid = !email.contains("Invalid email id")
}
return valid
}
private override init() {
}
}
And call function like this....
if SharedClass.sharedInstance. isValidEmail(email: emailTrimmedString!) == false {
SharedClass.sharedInstance.alert(view: self, title: "", message: "Please enter correct email")
//Your code here
} else {
//Code here
}

Here's an up to date playground compatible version that uses the standard library so you don't have to maintain a regex:
import Foundation
func isValid(email: String) -> Bool {
do {
let detector = try NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue)
let range = NSRange(location: 0, length: email.count)
let matches = detector.matches(in: email, options: .anchored, range: range)
guard matches.count == 1 else { return false }
return matches[0].url?.scheme == "mailto"
} catch {
return false
}
}
extension String {
var isValidEmail: Bool {
isValid(email: self)
}
}
let email = "test#mail.com"
isValid(email: email) // prints 'true'
email.isValidEmail // prints 'true'

Majority of the above regex examples fail to catch error when there are even basic problems with emails. For example
h..1#nyu.edu - consecutive dots
ab1234#.nyu.edu - dot after #
a.bcdnle12.#email.com - dot before #
.abc#email.com - starts with a dot
Here is a string extension I have used that uses regex with tighter rules.
extension String {
func isValidEmail() -> Bool {
let emailRegEx = "^(?!\\.)([A-Z0-9a-z_%+-]?[\\.]?[A-Z0-9a-z_%+-])+#[A-Za-z0-9-]{1,20}(\\.[A-Za-z0-9]{1,15}){0,10}\\.[A-Za-z]{2,20}$"
let emailPred = NSPredicate(format:"SELF MATCHES %#", emailRegEx)
return emailPred.evaluate(with: self)
}
}
Here is how we can write test case for it.
XCTAssertFalse("ab1234#.nyu.edu".isValidEmail())
XCTAssertTrue("valid_email#email.com".isValidEmail())

Since there are so many weird top level domain name now, I stop checking the length of the top domain...
Here is what I use:
extension String {
func isEmail() -> Bool {
let emailRegEx = "^[a-zA-Z0-9_.+-]+#[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$"
return NSPredicate(format:"SELF MATCHES %#", emailRegEx).evaluateWithObject(self)
}
}

Seems to work too...
let regex = "[A-Z0-9a-z._%+-]+#[A-Za-z0-9.-]+\\.[A-Za-z]{2,}"
func validate(email: String) -> Bool {
let matches = email.rangeOfString(regex, options: .RegularExpressionSearch)
if let _ = matches {
return true
}
return false
}

And for Swift 3:
extension String {
func isValidEmail() -> Bool {
let regex = try? NSRegularExpression(pattern: "^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+#[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])?)*$", options: .caseInsensitive)
return regex?.firstMatch(in: self, options: [], range: NSMakeRange(0, self.characters.count)) != nil
}
}

Related

find duplicate names on list and report to user [duplicate]

Declaration:
let listArray = ["kashif"]
let word = "kashif"
then this
contains(listArray, word)
Returns true but if declaration is:
let word = "Kashif"
then it returns false because comparison is case sensitive.
How to make this comparison case insensitive?
Xcode 8 • Swift 3 or later
let list = ["kashif"]
let word = "Kashif"
if list.contains(where: {$0.caseInsensitiveCompare(word) == .orderedSame}) {
print(true) // true
}
alternatively:
if list.contains(where: {$0.compare(word, options: .caseInsensitive) == .orderedSame}) {
print(true) // true
}
if you would like to know the position(s) of the element in the array (it might find more than one element that matches the predicate):
let indices = list.indices.filter { list[$0].caseInsensitiveCompare(word) == .orderedSame }
print(indices) // [0]
You can also use localizedStandardContains method which is case and diacritic insensitive and would match a substring as well:
func localizedStandardContains<T>(_ string: T) -> Bool where T : StringProtocol
Discussion This is the most appropriate method for doing user-level string searches, similar to how searches are done generally in the system. The search is locale-aware, case and diacritic insensitive. The exact list of search options applied may change over time.
let list = ["kashif"]
let word = "Káshif"
if list.contains(where: {$0.localizedStandardContains(word) }) {
print(true) // true
}
you can use
word.lowercaseString
to convert the string to all lowercase characters
For checking if a string exists in a array (case insensitively), please use
listArray.localizedCaseInsensitiveContainsString(word)
where listArray is the name of array
and word is your searched text
This code works in Swift 2.2
Swift 4
Just make everything (queries and results) case insensitive.
for item in listArray {
if item.lowercased().contains(word.lowercased()) {
searchResults.append(item)
}
}
You can add an extension:
Swift 5
extension Array where Element == String {
func containsIgnoringCase(_ element: Element) -> Bool {
contains { $0.caseInsensitiveCompare(element) == .orderedSame }
}
}
and use it like this:
["tEst"].containsIgnoringCase("TeSt") // true
Try this:
let loword = word.lowercaseString
let found = contains(listArray) { $0.lowercaseString == loword }
For checking if a string exists in a array with more Options(caseInsensitive, anchored/search is limited to start)
using Foundation range(of:options:)
let list = ["kashif"]
let word = "Kashif"
if list.contains(where: {$0.range(of: word, options: [.caseInsensitive, .anchored]) != nil}) {
print(true) // true
}
if let index = list.index(where: {$0.range(of: word, options: [.caseInsensitive, .anchored]) != nil}) {
print("Found at index \(index)") // true
}
swift 5, swift 4.2 , use the code in the below.
let list = ["kAshif"]
let word = "Kashif"
if list.contains(where: { $0.caseInsensitiveCompare(word) == .orderedSame }) {
print("contains is true")
}
SWIFT 3.0:
Finding a case insensitive string in a string array is cool and all, but if you don't have an index it can not be cool for certain situations.
Here is my solution:
let stringArray = ["FOO", "bar"]()
if let index = stringArray.index(where: {$0.caseInsensitiveCompare("foo") == .orderedSame}) {
print("STRING \(stringArray[index]) FOUND AT INDEX \(index)")
//prints "STRING FOO FOUND AT INDEX 0"
}
This is better than the other answers b/c you have index of the object in the array, so you can grab the object and do whatever you please :)
Expanding on #Govind Kumawat's answer
The simple comparison for a searchString in a word is:
word.range(of: searchString, options: .caseInsensitive) != nil
As functions:
func containsCaseInsensitive(searchString: String, in string: String) -> Bool {
return string.range(of: searchString, options: .caseInsensitive) != nil
}
func containsCaseInsensitive(searchString: String, in array: [String]) -> Bool {
return array.contains {$0.range(of: searchString, options: .caseInsensitive) != nil}
}
func caseInsensitiveMatches(searchString: String, in array: [String]) -> [String] {
return array.compactMap { string in
return string.range(of: searchString, options: .caseInsensitive) != nil
? string
: nil
}
}
My example
func updateSearchResultsForSearchController(searchController: UISearchController) {
guard let searchText = searchController.searchBar.text else { return }
let countries = Countries.getAllCountries()
filteredCountries = countries.filter() {
return $0.name.containsString(searchText) || $0.name.lowercaseString.containsString(searchText)
}
self.tableView.reloadData()
}
If anyone is looking to search values from within model class, say
struct Country {
var name: String
}
One case do case insensitive checks like below -
let filteredList = countries.filter({ $0.name.range(of: "searchText", options: .caseInsensitive) != nil })

How can we match string pattern like aa12ss9098 kind of registration number?

I am trying to match a string pattern that contains two characters, two numeric values , two characters and four numeric values. Can some please help me out
EG:- AP04ST9099
Below is the code I am trying for this
^\\[a-z]{2}-\\d{2}-\\[a-z]{2}-\\d{4}$
Please find below regex with solution.
extension String {
func matches(_ regex: String) -> Bool {
return self.range(of: regex, options: .regularExpression, range: nil, locale: nil) != nil
}
}
"AP04ST9099".matches("^[A-Za-z]{2}\\d{2}[A-Za-z]{2}\\d{4}$")//true
Sorter one:
func isValidValue(passedValue: String) -> Bool {
return NSPredicate(format:"SELF MATCHES %#","^[A-Z]{2}+[0-9]{2}+[A-Z]{2}+[0-9]{4}" ).evaluate(with: passedValue)
}
print(isValidValue(passedValue: "AP04ST9099"))
Swift 5
You can create a regular expression and confirm the string with regular expression.
Regular expression:
func isValidValue(passedValue: String) -> Bool {
let valueRegEx = "^[A-Z]{2}+[0-9]{2}+[A-Z]{2}+[0-9]{4}"
let valueTest = NSPredicate(format:"SELF MATCHES %#", valueRegEx)
return valueTest.evaluate(with: passedValue)
}
print(isValidValue(passedValue: "AP04ST9099"))

How to validate an email that it should contain at least some characters before "#"

in my app user can enter his email or phone number. I need to validate wether it's a phone number or email. if email entered it should be validated
ex: if user entered only "#mail.com" need to show not a valid mail to user.there should be at least 2 or three characters front of "#" here is my code
func validateEmail(enteredEmail:String) -> Bool {
let emailFormat = "[A-Z0-9a-z._%+-]+#[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
let emailPredicate = NSPredicate(format:"SELF MATCHES %#", emailFormat)
let email = emailPredicate.evaluate(with: enteredEmail)
if enteredEmail.contains("#") {
let characters = enteredEmail.components(separatedBy: "#")
if characters.count > 0 {
if characters[0].isEmpty {
return false
} else {
return true
}
}
}
return email ? email : enteredEmail.count == 10 //if email evaluation is false check for phone number count
}
This code is working fine for me but need a better solution. Thanks in advance
Why don't you simply modify the regex? It already checks if there is one or more characters before the # character. If you want to check for two or more use {2,}
let emailFormat = "[A-Z0-9a-z._%+-]{2,}#[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
Apart from the phone validation this is a swiftier way to apply the regex
The pattern is
2 or more characters from the character set [A-Z0-9a-z._%+-]
one #
one or more characters from the character set [A-Za-z0-9.-]
one .
2-64 upper- or lowercase alpha letters
func validateEmail(enteredEmail:String) -> Bool {
let emailFormat = "[A-Z0-9a-z._%+-]{2,}#[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
return enteredEmail.range(of: emailFormat, options: .regularExpression) != nil
}
If the entered string must exactly match the pattern use
let emailFormat = "^[A-Z0-9a-z._%+-]{2,}#[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}$"
Edit:
To include also the phone validation (just 10 digits) use
func validateEmail(enteredEmail:String) -> Bool {
let emailFormat = "^[A-Z0-9a-z._%+-]{2,}#[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}$"
guard enteredEmail.range(of: emailFormat, options: .regularExpression) == nil else { return true }
return enteredEmail.range(of: "^\\d{10}$", options: .regularExpression) != nil
}
use hasPrefix:
Example:
"hello dolly".hasPrefix("hello") // This will return true
"hello dolly".hasPrefix("abc") // This will return false
That way you can check if the first character is '#' or not and go on from there with an if-else statement.
Try This :
class func isValidEmail(testStr:String) -> Bool {
let emailRegEx = "[A-Z0-9a-z._%+-]+#[A-Za-z0-9.-]+\\.[A-Za-z]{2,}"
let emailTest = NSPredicate(format:"SELF MATCHES %#", emailRegEx)
return emailTest.evaluate(with: testStr)
}
the function is defined properly the issue that will be faced is when user type email like " #mail.com" as you are not trimming characters before validation.
Your code should be as follows:
if enteredEmail.contains("#")
{
let characters = enteredEmail.trimmingCharacters(in: CharacterSet.whitespaces).components(separatedBy: "#")
if characters.count > 0 {
if characters[0] > 2 {
return true
}else {
return false
}
}
}
The regex is fine: the error is in the return statement -and I actually don't like the code in the middle of the method-. You should validate phone numbers with another regex, not just string length, and then drop that if.
Here you have a cleaner example. You should change the phone number regex: it's for Spanish phone numbers.
import Foundation
func validateEmail(enteredEmail:String) -> Bool {
let emailFormat = "[A-Z0-9a-z._%+-]+#[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
let emailPredicate = NSPredicate(format:"SELF MATCHES %#", emailFormat)
let email = emailPredicate.evaluate(with: enteredEmail)
return email
}
func validatePhoneNumber(enteredPhoneNumber:String) -> Bool {
let phoneFormat = "\\A[0-9]{9}\\z"
let phonePredicate = NSPredicate(format:"SELF MATCHES %#", phoneFormat)
let phone = phonePredicate.evaluate(with: enteredPhoneNumber)
return phone
}
func validate(enteredString: String) -> Bool {
if validateEmail(enteredEmail: enteredString) == true {
return true
}
else {
return validatePhoneNumber(enteredPhoneNumber: enteredString)
}
}
validate(enteredString: "#email.com") // false
validate(enteredString: "some#email.com") // true: first regex
validate(enteredString: "666666666") // true: second regex
validate(enteredString: "some rubbish") // false

Swift : How to sort alphabetically but with letters first

i have a Realm results list:
subsList = RealmDB.objects(Downloads).filter("isActive = true").sorted("name", ascending: true)
And it display the results ordered, but the first results are "(item 1)" or 1-item, then comes A-item, B-item etc...
How can i sort it in a ways that A-item, B-item etc.. come first and (item 1), 1-item display at the end?
note: for those who don't know realm, it can take a NSPredicate for sorting
Thanks guys
EDIT:
Following the comments, I'm getting
-chloro
(+-)-1,2,3-octa
(amino)
1-propanol
acetone
benzin
dinoterb
TNT
And i need
acetone
benzin
dinoterb
TNT
-chloro
(+-)-1,2,3-octa
(amino)
1-propanol
This is just an example, fix the code to make it look better.
var arr = ["-chloro", "(+-)-1,2,3-octa", "(amino)", "1-propanol", "acetone", "benzin","dinoterb","TNT"]
func matches(for regex: String, in text: String) -> [String] {
do {
let regex = try NSRegularExpression(pattern: regex)
let nsString = text as NSString
let results = regex.matches(in: text, range: NSRange(location: 0, length: nsString.length))
return results.map { nsString.substring(with: $0.range)}
} catch let error {
print("invalid regex: \(error.localizedDescription)")
return []
}
}
arr.sort { (s1, s2) -> Bool in
let matched = matches(for: "[0-9,\\(,\\-]", in: s1)
let matched2 = matches(for: "[0-9\\(,\\-]", in: s2)
if matched.isEmpty && matched2.isEmpty {
return s1.lowercased() < s2.lowercased()
} else if matched.isEmpty && !matched2.isEmpty {
return true
} else if !matched.isEmpty && matched2.isEmpty {
return false
} else {
return matched.first! < matched2.first!
}
}
result -> ["acetone", "benzin", "dinoterb", "TNT", "(+-)-1,2,3-octa", "(amino)", "-chloro", "1-propanol"]

How to check in Swift if a Regular Expression is valid?

I'm using Regex to search in a textView. All works fine with simple strings but when I try to enter only a part of a regular expression operator I get a cash with the error:
Error Domain=NSCocoaErrorDomain Code=2048 "The value “\” is invalid." UserInfo={NSInvalidValue=\}
For example if I enter "\n" it works, but if I enter only "\" (without quotes), I get the crash. What I can't get to do is to find a way to check the expression before to use it.
I'm using this code partially based on this tutorial and this answer by Wiktor Stribiżew:
extension NSRegularExpression {
convenience init?(options: SearchOptions) {
let searchString = options.searchString
let isCaseSensitive = options.matchCase // set to true
let isWholeWords = options.wholeWords // set to false
let escapeMetacharacters = options.escapeMetacharacters // set to false
// handle case sensitive option
var regexOption: NSRegularExpressionOptions = .CaseInsensitive
if isCaseSensitive { // if it is match case remove case sensitive option
regexOption = []
}
// put the search string in the pattern
var pattern = searchString
if isWholeWords {
pattern = "(?<!\\w)" + NSRegularExpression.escapedPatternForString(searchString) + "(?!\\w)"
}
do {
try self.init(pattern: pattern, options: regexOption)
} catch {
print(error)
}
}
}
The first code below have shown unstable behaviour and you should not use it.
(Please see the latter part of this answer.)
Add one line to your failable initializer:
do {
try self.init(pattern: pattern, options: regexOption)
} catch {
print(error)
return nil //->you need to return nil to tell initialization failed
}
(I think Swift compiler should warn about this missing return nil. May be a bug of Swift?)
After that you can safely check the result if it's nil or not:
let sOpts = SearchOptions(searchString: "\\", replacementString: "", matchCase: false, wholeWords: false)
if let regex = NSRegularExpression(options: sOpts) {
//Use regex
print(regex)
} else {
//Process errors
print("Something bad in SearchOptions")
}
(I omitted escapeMetacharacters, as it's not used yet.)
As far as I tested, using static method has never crashed.
extension NSRegularExpression {
static func expresssionWith(options: SearchOptions) -> NSRegularExpression? {
let searchString = options.searchString
let isCaseSensitive = options.matchCase // set to true
let isWholeWords = options.wholeWords // set to false
// handle case sensitive option
var regexOption: NSRegularExpressionOptions = .CaseInsensitive
if isCaseSensitive { // if it is match case remove case sensitive option
regexOption = []
}
// put the search string in the pattern
var pattern = searchString
if isWholeWords {
pattern = "(?<!\\w)" + NSRegularExpression.escapedPatternForString(searchString) + "(?!\\w)"
}
do {
return try NSRegularExpression(pattern: pattern, options: regexOption)
} catch {
print(error)
return nil
}
}
}
let sOpts = SearchOptions(searchString: "\\", replacementString: "", matchCase: false, wholeWords: false)
if let regex = NSRegularExpression.expresssionWith(sOpts) {
//Use regex
print(regex)
} else {
//Process errors
print("Something bad in SearchOptions")
}

Resources