426

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.

9
  • 9
    translation should be straightforward. what part is giving you problems? Commented Aug 24, 2014 at 11:19
  • 18
    Don't forget to pray that none of your users has one of the new top level domains. E.g. .coffee Commented Feb 8, 2015 at 12:50
  • 4
    Regexes don't work for validating that users have entered their e-mail address. The only 100% correct way is to send an activation e-mail. See: I Knew How To Validate An Email Address Until I Read The RFC Commented Sep 18, 2017 at 9:03
  • 7
    This is a fascinating QA. It's almost certainly the "most wrong" QA on the whole site. The currently #1 answer with 600 votes (what?!) is absolutely, totally, wrong in every possible way (every individual line is completely wrong, and every concept and idea is wrong ........ !!!) Many of the other highly voted answers are either "completely wrong", "extremely shoody", or, plain broken and don't even compile. Further, while the nature of this Q calls for "elite regex engineering" many answers (highly voted!) feature appalling regex engineering. It's a really interesting QA!! Why?? Commented Feb 18, 2019 at 14:04
  • 3
    I agree with everything Fattie says - the difference between "good enough" and "near perfect": github.com/dhoerl/EmailAddressFinder. That said, I wrote a Mac project that constructs a regex, where each step in the construction references appropriate RFCs. It correctly processes several test suites of "edge cases" meant to trap incorrect regexes. But, as he said, it will allow "x@x" since that address is compliant with the specs. There is also a GitHub project (can't find the link) that offers a long long list of email servers, and probably has 99% coverage of ones you'd need to test against. Commented Jan 1, 2020 at 17:14

39 Answers 39

901

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
}
Sign up to request clarification or add additional context in comments.

24 Comments

wouldn't return emailTest.evaluateWithObject(testStr) be a lot more simpler and readable? Comparing to == true is a bit like Javascript.
It doesn't check if there is an extension available, a@a is already OK :(
this does not validate for [email protected]
This doesn't detect [email protected] or [email protected]. The answer below from @alexcristea does
It's quite funny that ............ as well as (1) the regex being utterly, totally incorrect (2) the regex (even within the context of what it's trying to do) has major errors (3) the Swift is wrong (4) even setting that aside, the style is totally wrong (5) not that it matters given all the rest but it doesn't even mention that you have to cache the predicate ... humorously, (6) there's still left over code ("calendar" - what?) from wherever it was copied from.
|
121

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.

7 Comments

the first with a valid regex. the others validate aa@aach to true
@netshark1000, only with upvotes, any answer will be on top. :)
NSRegularExpression is simpler to use than NSPredicate
It does not handle the two dots condition after the domain name. try this answer stackoverflow.com/a/53441176/5032981
@AzikAbdullah If you enter '[email protected]' then also it will validate
|
119

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() {

}

11 Comments

countElements is now count
xxx@yyy return true?
Same as Cullen SUN, foo@bar return true.
user@host without .tld is also a valid email address, e.g. root@localhost
Note that NSRange length property should use String utf16.count instead of characters.count
|
84

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 = "[email protected]"
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])+)\\])"

6 Comments

Wow, didn't know about emailregex.com. It is awesome!
Finally, one that filters [email protected]
it's working with exact -- [email protected] . it's not validate abc@abc
Ah, Finally.. :D
This seems to allow a one character domain suffix which is technically legal but all of those are held by the registrar so in practice it will always be invalid.
|
76

(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: [email protected])

__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.

10 Comments

With regards to point (4): how did you test with a lot of users? Did you track the users, that could not sign up with the commercial apps, because the regex did prevent them from using their email address? The only "reasonable" should be, what the spec (RFC) specifies or if this can not be achieved, then something that is more relaxed, but covers everything from the spec. If the users are not allowed to enter x@x, they will enter some [email protected] which will pass your/any regex.
This is an awesome answer. I was trying to validate the email id as the user types and toggle the enabled status of a button. With other regexes, the button gets enabled when i type aaa@aaa and then disabled when I add a '.' . This works as expected in my case.
Hi @Fattie, could you provide us a link where Apple explains that the predicate must be global? Thanks
@Fattie "user@домен.рф” for example. While absolutely valid, it doesn’t pass the validation.
@andbi - that's a very reasonable point. the example given is (I'm sorry) only for english-language alphabets. you're definitely right that in many situations, the programmer would add the foreign alphabet. that is a top point, thank you for mentioning it.
|
38

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

"[email protected]".isValidEmail

returns...

true

2 Comments

what is the point of repeating the repeated answer? which doesn't depend on any Swift 5 features
So many issues here. The {2,} at the end only applies to the last [A-Za-z]. This regex fails on valid emails and it passes invalid emails.
34

Here is a fuse of the two most up-voted answer with a basic correct enough 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)
        }
    }

4 Comments

.evaluateWithObject(self) has been changed to .evaluate(with: self)
So many issues here. The {2,20} at the end only applies to the last [A-Za-z]. This regex fails on valid emails and it passes invalid emails.
So many issues indeed terrible code. Ask ChatGPT for the correct answer.
@NicolasManzini ChatGPT gave me this same answer! :D
25

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 "[email protected]".isEmail { // true
    print("Hold the Door")
}

4 Comments

Note that NSRange length property should use String utf16.count instead of characters.count
Update Swift 4: 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: count)) return (firstMatch?.range.location != NSNotFound && firstMatch?.url?.scheme == "mailto") }
Absolutely brilliant! While other answers resolve the validation of emails in english letters, this beast validates emails in Arabic and other languages as well. Well done!
This is easily fooled: "mailto://www.google.com".isEmail == true, but it’s not an email.
15

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
    }
}

5 Comments

foo@bar returns true ?!
validates aa@aach to true
That's because the RFC validates these email adresses to true ;)
Note that NSRange length property should use String utf16.count instead of characters.count
it's really wrong / poor to not cache the predicate. it's the first thing Apple says about the issue in the doco. a glaring mistake made by most of the answers on the page.
9

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
        }
    }
}

4 Comments

does not work properly, it lets you add spaces after domain.
Note that NSRange length property should use String utf16.count instead of characters.count
@Fattie argue your statement. Your comment is very useless, suggest an improvement, propose a fix. Saying completely wrong is very stupid and underlies a close mentality
It instantly and completely fails the most trivial and obvious tests.
9

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"
[email protected]"
试@例子.测试.مثال.آزمایشی"
[email protected]"
[email protected]
  • 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:

Comments

9

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.

5 Comments

the emailRegEx you use is just plain wrong. It only allow for TLDs 2 to 4 characters long, while domains like .engineer exists.
Understood, I am not defending my answer but the level of the edit. Add a comment as above, down-vote, point to a better answer, add your own answer. It is not appropriate to substantially change an answer. I have added the diffusive RegEx for completeness.
Why oh why not just delete the answer then? What possible reason could there be to keep it here?
Oh god, so many answers here are wrong the same way. The regex should be enclosed in ^...$, otherwise it will match the regex anywhere in the input string!
So many issues here. The {2,64} at the end only applies to the last [A-Za-z]. This regex fails on valid emails and it passes invalid emails.
7

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)
    }
}

1 Comment

there are a few problems .. you can have, for example .. [email protected] with a weird dot there
7

EDIT: Updated to make the NSPredicate global.

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}"
    private static let __predicate = NSPredicate(format: "SELF MATCHES %@", __emailRegex)

    public var isEmail: Bool {
        return Self.predicate.evaluate(with: self)
    }
}

So its usage is simple:

let str = "[email protected]"
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.

3 Comments

But here the NSPredicate is no more global.
@LucasTegliabue this is a more than two year old answer, it could be wrong nowadays.
Doesn't change the fact that in your snippet the NSPredicate is no global. NSPredicate has the same lifecycle of the String instance, so is not global at all, or I am loosing something?
5

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
        }
    }
}

2 Comments

This seems to work fine for me. As far as I understand it you could even omit the 'A-Z' (capital letters) since you have the option .CaseInsensitive set anyway...
Note that NSRange length property should use String utf16.count instead of characters.count
5

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 
    {

    }

Comments

4

Apple's own documentation for the RegexBuilder type (introduced in Swift 5.7) now offers an example of minimal email validation, complete with named component capture.

(Copying here for posterity as they sometimes change their examples)

let word = OneOrMore(.word)
let emailPattern = Regex {
    Capture {
        ZeroOrMore {
            word
            "."
        }
        word
    }
    "@"
    Capture {
        word
        OneOrMore {
            "."
            word
        }
    }
}

let text = "My email is [email protected]."
if let match = text.firstMatch(of: emailPattern) {
    let (wholeMatch, name, domain) = match.output
    // wholeMatch is "[email protected]"
    // name is "my.name"
    // domain is "example.com"
}

Despite this question already having too many answers, I felt this was worth sharing since it:

  1. Uses the latest Swift APIs
  2. Does not overcomplicate the regex logic (a trap for email validation)
  3. Comes directly from Apple (almost like they knew)

Comments

2

@JeffersonBe's answer is close, but returns true if the string is "something containing [email protected] 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
}

2 Comments

Note that NSRange length property should use String utf16.count instead of characters.count
"mailto://www.google.com".isEmail == true, but not an email.
2

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:

"[email protected]".isValidEmail //true
"b@bb".isValidEmail //false

You can extend following extension to anything you need: isValidPhoneNumber, isValidPassword etc...

1 Comment

Note that NSRange length property should use String utf16.count instead of characters.count
2

Majority of the above regex examples fail to catch error when there are even basic problems with emails. For example

  1. [email protected] - consecutive dots
  2. [email protected] - dot after @
  3. [email protected] - dot before @
  4. [email protected] - 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("[email protected]".isValidEmail())
XCTAssertTrue("[email protected]".isValidEmail())

Comments

1

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: "[email protected]")) {
   //email is valid
}

SwiftCop is the library... hope it help!

Comments

1

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
    }
}

2 Comments

abcd@a is passing with this regex. You should fix it.
Note that NSRange length property should use String utf16.count instead of characters.count
1

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.

1 Comment

Note that NSRange length property should use String utf16.count instead of characters.count
1

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
}

2 Comments

Changing to use the Regex from alexcristea' answer, it's perfect solution.
So many issues here. The {2,64} at the end only applies to the last [A-Za-z]. This regex fails on valid emails and it passes invalid emails.
1

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)
        }
    }

Comments

1

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. [email protected] 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)[email protected] are both equivalent to [email protected];

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)

2 Comments

Thanks for the first part with contains check and return false
So many issues here. The {2,64} at the end only applies to the last [A-Za-z]. This regex fails on valid emails and it passes invalid emails.
1

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
}

Comments

1

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 = "[email protected]"
isValid(email: email) // prints 'true'
email.isValidEmail // prints 'true'

1 Comment

NSDataDetector solutions are unreliable because they validate strings like: mailto://www.google.com, which is not an email.
1
In Swift 5.7, with the help of the Regex class, we can validate email addresses in a simple and efficient manner
        private func isValidEmail(_ email: String) -> Bool {
              guard let emailRegex = try? Regex("[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}")  
              else { return false }
              return email.firstMatch(of: emailRegex) != nil
           }
Also we can use a property wrapper to make it more efficient:
    import Foundation
    
    @propertyWrapper
    struct ValidEmail {
        var wrappedValue: String {
            didSet {
                if !isValidEmail(wrappedValue) {
                    wrappedValue = ""
                }
            }
        }
        
        init(wrappedValue: String) {
            if isValidEmail(wrappedValue) {
                self.wrappedValue = wrappedValue
            } else {
                self.wrappedValue = ""
            }
        }
        
        private func isValidEmail(_ email: String) -> Bool {
            let emailRegEx = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
            let emailTest = NSPredicate(format:"SELF MATCHES %@", emailRegEx)
            return emailTest.evaluate(with: email)
        }
    }
    // Usage:
    
    struct User {
        var name: String
        @ValidEmail var email: String
    }
    
    var user = User(name: "John Doe", email: "[email protected]")
    print(user.email) // Prints: [email protected]
    
    user.email = "invalid email"
    print(user.email) // Prints: ""

1 Comment

So many issues here. The {2,64} at the end only applies to the last [A-Za-z]. This regex fails on valid emails and it passes invalid emails.
0

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)
    } 
}

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.