How to validate an e-mail address in swift?

243,463

Solution 1

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
}

Solution 2

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

}

Solution 3

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.

Solution 4

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])+)\\])"

Solution 5

NOWADAYS YOU >> MUST NOT << USE REGEX TO "VALIDATE" EMAILS. A SOLUTION IS NOW BUILT IN TO SWIFT/IOS:

There are now thousands of tutorials around on the obvious way to check emails these days:

https://multithreaded.stitchfix.com/blog/2016/11/02/email-validation-swift/


For historians looking at how it was done with regex:

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.


NOWADAYS YOU >> MUST NOT << USE REGEX TO "VALIDATE" EMAILS. A SOLUTION IS NOW BUILT IN TO SWIFT/IOS:

There are now thousands of tutorials around on the obvious way to check emails these days:

https://multithreaded.stitchfix.com/blog/2016/11/02/email-validation-swift/

Share:
243,463
giorgio.nocera
Author by

giorgio.nocera

Updated on July 24, 2022

Comments

  • giorgio.nocera
    giorgio.nocera almost 2 years

    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.

  • Samuel Ev
    Samuel Ev over 7 years
    Wow, didn't know about emailregex.com. It is awesome!
  • Ben Sullivan
    Ben Sullivan over 7 years
    Finally, one that filters [email protected]
  • Fattie
    Fattie over 7 years
    there are a few problems .. you can have, for example .. [email protected] with a weird dot there
  • Gunhan
    Gunhan about 7 years
    abcd@a is passing with this regex. You should fix it.
  • ittgung
    ittgung over 6 years
    Changing to use the Regex from alexcristea' answer, it's perfect solution.
  • ami rt
    ami rt over 6 years
    whoever vote out my answer, kindly check your knowledge. I have applied this regex in many code and my friends of mine is using this regex and it works great..Before vote out my answer kindly do comment and let me know what is wrong with this regex.
  • Hugal31
    Hugal31 over 6 years
    I think I can answer: Your regex is to simple and doesn't match the RFC. For example, emails can have quotes and even spaces in the first part! Look at haacked.com/archive/2007/08/21/…
  • ami rt
    ami rt over 6 years
    Sorry, brother, I think you should check google email validation, there is no way to add Space in the first part of an email, and if my regex is wrong then why doesn't anyone post write and perfect regex.
  • Hugal31
    Hugal31 over 6 years
    According to the RFC 5322, "Hello world!"@example.com is a valid email. Indeed, it is almost impossible to make a valid regex. Not every mail provider will stick to google email validation.
  • ami rt
    ami rt over 6 years
    Thats what I want to listen, and thats why I mentioned in bold heading that above regex is like Google. Thanks
  • Leo Dabus
    Leo Dabus about 6 years
    Note that NSRange length property should use String utf16.count instead of characters.count
  • Leo Dabus
    Leo Dabus about 6 years
    Note that NSRange length property should use String utf16.count instead of characters.count
  • Leo Dabus
    Leo Dabus about 6 years
    Note that NSRange length property should use String utf16.count instead of characters.count
  • Leo Dabus
    Leo Dabus about 6 years
    Note that NSRange length property should use String utf16.count instead of characters.count
  • Leo Dabus
    Leo Dabus about 6 years
    Note that NSRange length property should use String utf16.count instead of characters.count
  • Leo Dabus
    Leo Dabus about 6 years
    Note that NSRange length property should use String utf16.count instead of characters.count
  • Leo Dabus
    Leo Dabus about 6 years
    Note that NSRange length property should use String utf16.count instead of characters.count
  • Duan Nguyen
    Duan Nguyen about 6 years
    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") }
  • Roman
    Roman almost 6 years
    Does it support new TLDs like .engineer?
  • thetrutz
    thetrutz almost 6 years
    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.
  • mAc
    mAc over 5 years
    failed if u write ".com" twice in the end. Check atleast very common cases before mentioning 100% working and TESTED
  • Anil Gupta
    Anil Gupta over 5 years
    it's working with exact -- [email protected] . it's not validate abc@abc
  • Ümañg ßürmån
    Ümañg ßürmån about 4 years
    Ah, Finally.. :D
  • rommex
    rommex about 4 years
    what is the point of repeating the repeated answer? which doesn't depend on any Swift 5 features
  • humblePilgrim
    humblePilgrim over 3 years
    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.
  • Andy Weinstein
    Andy Weinstein over 3 years
    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.
  • Lucas Tegliabue
    Lucas Tegliabue over 3 years
    Hi @Fattie, could you provide us a link where Apple explains that the predicate must be global? Thanks
  • Lucas Tegliabue
    Lucas Tegliabue over 3 years
    But here the NSPredicate is no more global.
  • Alejandro Iván
    Alejandro Iván over 3 years
    @LucasTegliabue this is a more than two year old answer, it could be wrong nowadays.
  • The iOSDev
    The iOSDev over 3 years
    Thanks for the first part with contains check and return false
  • Lucas Tegliabue
    Lucas Tegliabue over 3 years
    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?
  • akashlal.com
    akashlal.com almost 3 years
    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!
  • andbi
    andbi almost 3 years
    Warning. This doesn’t work with IDNs (international domain names)
  • Fattie
    Fattie almost 3 years
    hi @andbi . It works absolutely perfectly with "international" domain names. Could you give a clear example of what you mean?
  • andbi
    andbi almost 3 years
    @Fattie "user@домен.рф” for example. While absolutely valid, it doesn’t pass the validation.
  • Fattie
    Fattie over 2 years
    @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.
  • Jano
    Jano about 2 years
    NSDataDetector solutions are unreliable because they validate strings like: mailto://www.google.com, which is not an email.
  • Jano
    Jano about 2 years
    This is easily fooled: "mailto://www.google.com".isEmail == true, but it’s not an email.
  • Jano
    Jano about 2 years
    "mailto://www.google.com".isEmail == true, but not an email.
  • trndjc
    trndjc about 2 years
    Email addresses can't start or end with dots (unless if they're in quotes) yet the link provided in your answer validates emails that start and end with dots. What am I missing?
  • Jimbo
    Jimbo almost 2 years
    Would you mind sharing a link to the docs for where Apple states that an instance of NSPredicate must be global? I can't find it here.
  • Aiden McCollum
    Aiden McCollum almost 2 years
    This worked great for me once I added a ^ between the " and ( at the beginning!