416

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.

Naresh
  • 16,698
  • 6
  • 112
  • 113
giorgionocera
  • 6,428
  • 6
  • 20
  • 17
  • 8
    translation should be straightforward. what part is giving you problems? – Sulthan Aug 24 '14 at 11:19
  • The problem was " NSRegularExpression *regEx = [[NSRegularExpression alloc] initWithPattern:regExPattern options:NSRegularExpressionCaseInsensitive error:nil]; " – giorgionocera Aug 24 '14 at 11:44
  • 16
    Don't forget to pray that none of your users has one of the new top level domains. E.g. `.coffee` – Matthias Bauch Feb 08 '15 at 12:50
  • 1
    The regexp is not good. Too restrictive. Use "^[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])?)*$" instead – Antzi Jun 15 '15 at 11:02
  • 2
    @Antzi: I checked with "someone@gmail" and your regex returned true. – AnLT Jul 07 '16 at 15:36
  • 3
    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](http://haacked.com/archive/2007/08/21/i-knew-how-to-validate-an-email-address-until-i.aspx/) – mouviciel Sep 18 '17 at 09:03
  • 5
    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?? – Fattie Feb 18 '19 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. – David H Jan 01 '20 at 17:14
  • 1
    Amazingly this QA now has ***eight hundred votes on a totally, absolutely, completely wrong answer!*** This is the "famously wrong answer" on SO. – Fattie Sep 11 '20 at 14:35

40 Answers40

887

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
}
Zandor Smith
  • 558
  • 6
  • 25
Maxim Shoustin
  • 77,483
  • 27
  • 203
  • 225
  • 7
    wouldn't `return emailTest.evaluateWithObject(testStr)` be a lot more simpler and readable? Comparing to `== true` is a bit like Javascript. – Sulthan Aug 24 '14 at 11:55
  • @Sulthan Yes, in case if you don't want to debug it or leave logs. Actually you can write it with one row – Maxim Shoustin Aug 24 '14 at 11:57
  • @Sulthan but you are right about `== true`, habit from Javascript :) – Maxim Shoustin Aug 24 '14 at 12:01
  • @MaximShoustin Writing `if (booleanCondition) { return true } else { return false }` is a bad habit in most languages, including javascript . If you want to add a log or have a value to inspect during debugging, you should assign that boolean to a variable and return that variable. Also mind the semicolons which are not written in Swift. – Sulthan Aug 24 '14 at 12:13
  • This is a really nice example. However, it should be worth nothing though that with the advent of all the new TLDs such as .pizza or .google that the final part of the regex needs adjusting. – Keab42 May 13 '15 at 11:33
  • 20
    It doesn't check if there is an extension available, a@a is already OK :( – CularBytes Jul 12 '15 at 19:55
  • @MaximShoustin Try entering something like xyz@xy and still it returns true. But this is wrong. Using Swift 1.2. and using your above code. that failed just replace the emailRegEx with let emailRegEx = "^[_A-Za-z0-9-]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$" And it will work fine even for xyz@xy and will return false – Rajan Maheshwari Sep 12 '15 at 13:57
  • 2
    it accepts emails starting with a dot, however its a RegEx issue – t0a0 Mar 21 '16 at 11:07
  • 8
    this does not validate for test@test...com – Alan Oct 27 '16 at 17:23
  • 3
    This doesn't detect email.@invalid.com or email@.invalid.com. The answer below from @alexcristea does – Ben Sullivan Nov 23 '16 at 09:42
  • 1
    `qwe@qwe@qwe.qwe` is OK too :( – Nike Kov Jun 23 '17 at 13:42
  • 1
    I have to add start and end of regular expression like ^[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$ other wise it is also allowing abc^asd@da.com – Rakesh Yembaram Jul 04 '17 at 16:07
  • this let's a user accidentally enter in .con instead of .com which is a very common mistake in the app I wrote ... – Lloyd Rochester Feb 05 '18 at 06:05
  • @LloydRochester well, this is not common case. `boy@somecomp.con` is a valid email. – Maxim Shoustin Feb 05 '18 at 09:49
  • @MaximShoustin what I meant to really say is beware these answers don't validate top level domains and as such will allow a `.con` email instead of a `.com`. On mobile devices users will make this mistake a lot because the `n` character is close to to the `m`. This will certainly make users confused in say a user signup field where they're expecting an email confirmation. – Lloyd Rochester Feb 05 '18 at 13:34
  • 7
    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. – Fattie May 01 '19 at 12:27
  • None of the provided answers, including the accepted one, support IDNs - international domain names. – andbi Jun 23 '21 at 21:04
  • doesn't pass all the tests https://stackoverflow.com/questions/297420/list-of-email-addresses-that-can-be-used-to-test-a-javascript-validation-script – Peter Lapisu Jul 16 '21 at 10:14
  • @Fattie funny enough, yours is also a wrong suggestion, easily fooled by links that are not email addresses: "mailto://www.google.com" – Jano Mar 01 '22 at 12:45
  • hi @Jano (1) funnily enough, I don't know what you mean? (Are you referring to the comment about "email validation swift") (2) my answer is not a suggestion, it's the correct answer :) (3) you can easily test it with 'mailto://www.google.com'. Cheers – Fattie May 30 '22 at 13:37
  • @Fattie Apologies for the confusion. I was probably referring to the preamble data detector solution you posted. Other than that, your code is a reasonable answer. – Jano May 31 '22 at 17:15
  • It is I who should apologize! Cheers ... – Fattie Jun 01 '22 at 11:50
120

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

}
Arsonik
  • 2,276
  • 1
  • 16
  • 24
119

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.

Andrej
  • 7,266
  • 4
  • 38
  • 57
Nazik
  • 8,696
  • 27
  • 77
  • 123
80

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])+)\\])"
alexcristea
  • 2,316
  • 16
  • 18
71

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

Fattie
  • 27,874
  • 70
  • 431
  • 719
  • 1
    Does it support new TLDs like .engineer? – Roman Jun 26 '18 at 12:45
  • 2
    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 garbage@example.com which will pass your/any regex. – thetrutz Jul 31 '18 at 12:40
  • 2
    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. – humblePilgrim Sep 11 '20 at 14:00
  • 4
    Hi @Fattie, could you provide us a link where Apple explains that the predicate must be global? Thanks – Lucas Tegliabue Dec 03 '20 at 15:05
  • 1
    Warning. This doesn’t work with IDNs (international domain names) – andbi Jun 23 '21 at 21:02
  • 1
    hi @andbi . It works absolutely perfectly with "international" domain names. Could you give a clear example of what you mean? – Fattie Jun 24 '21 at 11:23
  • 2
    @Fattie "user@домен.рф” for example. While absolutely valid, it doesn’t pass the validation. – andbi Jun 24 '21 at 20:13
  • 2
    @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. – Fattie Aug 05 '21 at 15:29
  • 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? – trndjc Mar 11 '22 at 00:34
  • 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](https://developer.apple.com/documentation/foundation/nspredicate). – Jimbo May 11 '22 at 12:56
40

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
Ken Mueller
  • 3,659
  • 3
  • 21
  • 33
  • 12
    what is the point of repeating the repeated answer? which doesn't depend on any Swift 5 features – rommex Mar 10 '20 at 09:06
30

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)
        }
    }
Nicolas Manzini
  • 8,379
  • 6
  • 63
  • 81
24

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")
}
JeffersonBe
  • 559
  • 6
  • 10
  • 2
    Note that NSRange length property should use String utf16.count instead of characters.count – Leo Dabus Feb 22 '18 at 18:54
  • 1
    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") } – Duan Nguyen Apr 12 '18 at 08:31
  • 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! – akashlal.com Jun 15 '21 at 15:50
  • This is easily fooled: "mailto://www.google.com".isEmail == true, but it’s not an email. – Jano Mar 01 '22 at 12:30
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
    }
}
lesyk
  • 3,979
  • 3
  • 25
  • 39
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.

mojuba
  • 11,842
  • 9
  • 51
  • 72
zaph
  • 111,848
  • 21
  • 189
  • 228
  • 2
    the emailRegEx you use is just plain wrong. It only allow for TLDs 2 to 4 characters long, while domains like `.engineer` exists. – Antzi Jun 15 '15 at 14:30
  • 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. – zaph Jun 15 '15 at 14:48
  • Why oh why not just delete the answer then? *What possible reason* could there be to keep it here? – Fattie Oct 15 '19 at 15:04
  • 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! – mojuba May 08 '22 at 15:23
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
        }
    }
}
Andrea.Ferrando
  • 987
  • 13
  • 23
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)
    }
}
Marlon Ruiz
  • 1,774
  • 16
  • 14
6

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.

Alejandro Iván
  • 3,969
  • 1
  • 21
  • 30
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
        }
    }
}
hichris123
  • 10,145
  • 15
  • 56
  • 70
lee5783
  • 434
  • 4
  • 12
  • 1
    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... – AZOM Mar 30 '16 at 12:04
  • Note that NSRange length property should use String utf16.count instead of characters.count – Leo Dabus Feb 22 '18 at 18:53
4

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 
    {

    }
ikbal
  • 1,110
  • 12
  • 21
4

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:

Jano
  • 62,815
  • 21
  • 164
  • 192
2

@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
}
Kudit
  • 4,212
  • 2
  • 26
  • 32
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:

"b@bb.pl".isValidEmail //true
"b@bb".isValidEmail //false

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

Bartłomiej Semańczyk
  • 59,234
  • 49
  • 233
  • 358
2

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

  1. h..1@nyu.edu - consecutive dots
  2. ab1234@.nyu.edu - dot after @
  3. a.bcdnle12.@email.com - dot before @
  4. .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())
Hassaan Fayyaz
  • 189
  • 1
  • 4
2

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 my.name@example.com."
if let match = text.firstMatch(of: emailPattern) {
    let (wholeMatch, name, domain) = match.output
    // wholeMatch is "my.name@example.com"
    // 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)
Nathan Hosselton
  • 1,089
  • 1
  • 12
  • 16
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: "test@test.com")) {
   //email is valid
}

SwiftCop is the library... hope it help!

Andres
  • 11,439
  • 12
  • 48
  • 87
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
    }
}
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.

Krutarth Patel
  • 3,407
  • 6
  • 27
  • 54
Andrei Popa
  • 159
  • 9
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
}
Gefilte Fish
  • 1,600
  • 1
  • 18
  • 17
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)
        }
    }
Abdelahad Darwish
  • 5,969
  • 1
  • 17
  • 35
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. 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)
JavaBeast
  • 766
  • 3
  • 11
  • 28
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
}
Naresh
  • 16,698
  • 6
  • 112
  • 113
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 = "test@mail.com"
isValid(email: email) // prints 'true'
email.isValidEmail // prints 'true'
Fero
  • 567
  • 8
  • 25
  • NSDataDetector solutions are unreliable because they validate strings like: `mailto://www.google.com`, which is not an email. – Jano Mar 01 '22 at 12:20
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:

@propertyWrapper
struct EmailPropertyWrapper {
  private var _value: String
  var wrappedValue: String {
    get { return isValidEmail(_value) ? _value : String() }
    set { _value = newValue }
  }
  
  init(email: String) {
    _value = email
  }

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

struct User {
  var name: String
  @EmailPropertyWrapper var email: String
  
  func validateProperty() -> Bool {
    if name.isEmpty || email.isEmpty { return false }
    return true
  }
}

let user = User(name: "Sy", email: .init(email: "wwdc@icloud.com"))

print(user.validateProperty())
SomuYadav
  • 125
  • 6
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)
    } 
}
snowmen10
  • 29
  • 2
  • 5
0

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
}
Logicopolis
  • 81
  • 1
  • 4
0

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
    }
}
Dani Pralea
  • 4,545
  • 2
  • 31
  • 49
0

Perfect Regex like Google Email

"^[A-Z0-9a-z][a-zA-Z0-9_.-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}"
Fattie
  • 27,874
  • 70
  • 431
  • 719
ami rt
  • 935
  • 6
  • 11
  • 2
    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. – ami rt Jan 11 '18 at 05:23
  • 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 https://haacked.com/archive/2007/08/21/i-knew-how-to-validate-an-email-address-until-i.aspx/ – Hugal31 Jan 24 '18 at 09:38
  • 1
    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. – ami rt Jan 24 '18 at 09:44
  • 1
    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. – Hugal31 Jan 24 '18 at 09:58
  • 1
    Thats what I want to listen, and thats why I mentioned in bold heading that above regex is like Google. Thanks – ami rt Jan 24 '18 at 10:14
-1

Or you can have extension for optional text of UITextField:

how to use:

if  emailTextField.text.isEmailValid() {
      print("email is valid")
}else{
      print("wrong email address")
}

extension:

extension Optional where Wrapped == String {
    func isEmailValid() -> Bool{
        guard let email = self else { return false }
        let emailPattern = "[A-Za-z-0-9.-_]+@[A-Za-z0-9]+\\.[A-Za-z]{2,3}"
        do{
            let regex = try NSRegularExpression(pattern: emailPattern, options: .caseInsensitive)
            let foundPatters = regex.numberOfMatches(in: email, options: .anchored, range: NSRange(location: 0, length: email.count))
            if foundPatters > 0 {
                return true
            }
        }catch{
            //error
        }
        return false
    }
}
Maciej Chrzastek
  • 1,380
  • 12
  • 18
-1

For anyone who is still looking for an answer to this, please have a look at the following framework;

ATGValidator

It is a rule based validation framework, which handles most of the validations out of box. And top it all, it has form validator which supports validation of multiple textfields at the same time.

For validating an email string, use the following;

"abcd.hhs@some.com".satisfyAll(rules: [StringRegexRule.email]).status

If you want to validate an email from textfield, try below code;

textfield.validationRules = [StringRegexRule.email]
textfield.validationHandler = { result in
    // This block will be executed with relevant result whenever validation is done.
    print(result.status, result.errors)
}
// Below line is to manually trigger validation.
textfield.validateTextField()

If you want to validate it while typing in textfield or when focus is changed to another field, add one of the following lines;

textfield.validateOnInputChange(true)
// or
textfield.validateOnFocusLoss(true)

Please check the readme file at the link for more use cases.

Suran
  • 1,209
  • 1
  • 17
  • 21
-1

Swift 5 - Scalable Validation Layer

Using this layer you will get amazing validations over any text field easily.

Just follow the following process.

1. Add these enums:

import Foundation

enum ValidatorType
{
    case email
    case name
    // add more cases ...
}

enum ValidationError: Error, LocalizedError
{
    case invalidUserName
    case invalidEmail
    // add more cases ...

    var localizedDescription: String
    {
        switch self
        {
        case .invalidEmail:
            return "Please kindly write a valid email"    
        case .invalidUserName:
            return "Please kindly write a valid user name"
        }
    }
}

2. Add this functionality to String:

extension String
{
    // MARK:- Properties

    var isValidEmail: 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: self)
    }

    // MARK:- Methods

    func validatedText(_ validationType: ValidatorType) throws
    {
        switch validationType
        {
            case .name:
                try validateUsername()
            case .email:
                try validateEmail()
        }
    }

    // MARK:- Private Methods

    private func validateUsername() throws
    {
        if isEmpty
        {
            throw ValidationError.invalidUserName
        }
    }

    private func validateEmail() throws
    {
        if !isValidEmail 
        {
            throw ValidationError.invalidEmail
        }

        // add more validations if you want like empty email
    }
}

3. Add the following functionality to UITextField:

import UIKit

extension UITextField
{
    func validatedText(_ validationType: ValidatorType) throws
    {
        do
        {
            try text?.validatedText(validationType)
        }
        catch let validationError
        {
            shake()
            throw validationError
        }
    }

    // MARK:- Private Methods

    private func shake()
    {
        let animation = CABasicAnimation(keyPath: "position")
        animation.duration = 0.1
        animation.repeatCount = 5
        animation.fromValue = NSValue(cgPoint: CGPoint(x: center.x + 6, y: center.y))
        animation.toValue = NSValue(cgPoint: CGPoint(x: center.x - 6, y: center.y))
        layer.add(animation, forKey: "position")
    }
}

Usage

import UIKit

class LoginVC: UIViewController
{
    // MARK: Outlets

    @IBOutlet weak var textFieldEmail: UITextField!

    // MARK: View Controller Life Cycle

    override func viewDidLoad()
    {
        super.viewDidLoad()
    }

    // MARK: Methods

    private func checkEmail() -> Bool
    {
        do
        {
            try textFieldEmail.validatedText(.email)
        }
        catch let error
        {
            let validationError = error as! ValidationError
            // show alert to user with: validationError.localizedDescription
            return false
        }

        return true
    }

    // MARK: Actions

    @IBAction func loginTapped(_ sender: UIButton)
    {
        if checkEmail()
        {
            let email = textFieldEmail.text!
            // move safely ...
        }
    }
}
Essam Fahmi
  • 1,920
  • 24
  • 31
-2
//Email validation
func validateEmail(enterEmail: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:enterEmail)
}

100% working and tested

Mr.Javed Multani
  • 12,549
  • 4
  • 53
  • 52
  • 4
    failed if u write ".com" twice in the end. Check atleast very common cases before mentioning 100% working and TESTED – mAc Sep 13 '18 at 13:56
-2

I like to create extension

   extension String {

func isValidateEmail() -> 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: self)
}

}

usage:

if emailid.text!.isValidateEmail() == false(){
 //do what ever you want if string is not matched.

}
siva kumar
  • 2,745
  • 6
  • 19
  • 29
-2

Swift 5

 func isValidEmailAddress(emailAddressString: String) -> Bool {

 var returnValue = true
 let emailRegEx = "[A-Z0-9a-z.-_]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,3}"

 do {
        let regex = try NSRegularExpression(pattern: emailRegEx)
        let nsString = emailAddressString as NSString
        let results = regex.matches(in: emailAddressString, range: NSRange(location: 0, length: nsString.length))

        if results.count == 0
        {
            returnValue = false
        }

    } catch let error as NSError {
        print("invalid regex: \(error.localizedDescription)")
        returnValue = false
    }

    return  returnValue
}

Then:

let validEmail = isValidEmailAddress(emailAddressString: "your@email.com")
print(validEmail)
Arafin Russell
  • 1,487
  • 1
  • 18
  • 37
-3

Here is a very simple way available in current Swiftmailer. Most of the other answers are old and reinvent the wheel.

As per Swiftmailer documentation: https://swiftmailer.symfony.com/docs/messages.html#quick-reference

use Egulias\EmailValidator\EmailValidator;
use Egulias\EmailValidator\Validation\RFCValidation;

$validator = new EmailValidator();
$validator->isValid("example@example.com", new RFCValidation()); //true

This is by far the simplest and most robust approach, imo. Just install via Composer the Egulias\EmailValidator library which should already be brought in as a dependency of SwiftMailer anyway.

JaneDoe
  • 430
  • 1
  • 4
  • 16