Our DNA is written in Objective-C

E-Mail Validation

For the ProductLayer sign-up form I wanted to make sure that the user can only send a sign up if the email address is valid. If you google for ways to validate an email address you most often solutions involving regular expressions. But since I don’t trust a RegEx unless I know it by heart, I implemented the validation with Apple’s own NSDataDetector for links.

BSA Banner

The most highly voted result on a related question on Stack Overflow looks like this:

- (BOOL)validateEmail:(NSString *)candidate 
    NSString *emailRegex =
    NSPredicate *emailTest = [NSPredicate predicateWithFormat:@"SELF MATCHES[c] %@", 
    return [emailTest evaluateWithObject:candidate];

The code might be readable, but such a regular expression makes shivers run up and down my spine.

NSDataDetector is a subclass of NSRegularExpression which internally knows the patterns for a great variety of things it can detect: Phone numbers, addresses, dates, transit information and many more. This is also the way how Apple detects mail addresses and links in Safari or Mail.app.

You can create a data detector for type NSTextCheckingTypeLink and it will include both web addresses as well as email addresses. All that remains are some cursory checks to further narrow down the validation.

- (BOOL)_isValidEmail:(NSString *)email
   if (![email length])
      return NO;
   NSRange entireRange = NSMakeRange(0, [email length]);
   NSDataDetector *detector = [NSDataDetector dataDetectorWithTypes:NSTextCheckingTypeLink
   NSArray *matches = [detector matchesInString:email options:0 range:entireRange];
   // should only a single match
   if ([matches count]!=1)
      return NO;
   NSTextCheckingResult *result = [matches firstObject];
   // result should be a link
   if (result.resultType != NSTextCheckingTypeLink)
      return NO;
   // result should be a recognized mail address
   if (![result.URL.scheme isEqualToString:@"mailto"])
      return NO;
   // match must be entire string
   if (!NSEqualRanges(result.range, entireRange))
      return NO;
   // but schould not have the mail URL scheme
   if ([email hasPrefix:@"mailto:"])
      return NO;
   // no complaints, string is valid email address
   return YES;

The data detector infers the mailto: URL scheme and possible multiple link matches in the passed string. The ifs make sure that “mailto:something” is not recognized as a valid email address.

Granted the above is more code to copy/paste but I have a better feeling using the on-board method for detecting a valid email address. Here I have some hope that Apple will make sure the underlying regular expressions are solid.

Categories: Recipes


  1. I have doubts about `if (![matches count]==1)` part

    `![matches count]` (if none found) would convert 0 (NO) to 1 (YES) which would turn condition to true, so if one match is found then you get a false negative.

    May be you meant `if ([matches count] !=1 )` which would match 0 or more than 1 match being found?

  2. Eimantas Vaičiūnas, thanks for spotting my mistake.

  3. This code is not spotting the error in case of ab..c@example.com . Please help!


  1. NSScanner Tutorial for OS X
  2. NSScanner Tutorial for OS X | 神刀安全网
  3. NSScanner Tutorial for OS X | All Of App

Leave a Comment

%d bloggers like this: