Email verification is a critical component of any modern digital communication platform. Inaccurate email addresses can lead to a multitude of issues, from undelivered messages, and spam to inability to verify an account. As an iOS developer, it’s essential to verify the email addresses provided by users to prevent these problems from occurring. Fortunately, Apple’s NSDataDetector class can be used to verify email addresses quickly and efficiently. This class allows you to detect and extract various types of data, including phone numbers, addresses, and email addresses, from a string.
Why use NSDataDetector as a Email Validator?
Validating email often comes with the headache of writing self-scripted regular expressions or putting faith on regular expressions you grabbed from other sites. And even with a well sculpted regular expression we know determining a valid email address isn’t quite as simple as determining that <anything>@<something>.<another-thing>
. So why go through all that pain when you could validate email addresses using apple’s very own regular expression-based methods?
Beneath the magic, the NSDataDetector
class actually uses regrex, the difference is you don’t have to try to validate the regrex rules yourself. You can be rest assured a regrex rule used in apple’s own mail app will be more than sufficient to handle your use case. Let’s see it in action.
NSDataDetector Email Validation 🌟
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class Validator {
enum EmailState {
case valid
case empty
case invalid
}
static func validate(email: String) -> EmailState {
// 1
guard !email.isEmpty else {return .empty }
// 2
let trimmedEmail = email.trimmingCharacters(in: .whitespacesAndNewlines)
//3
let detector = try? NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue)
//4
let range = NSRange(trimmedEmail.startIndex..<trimmedEmail.endIndex, in: trimmedEmail)
//5
guard let matches = detector?.matches(in: trimmedEmail, options: [], range: range) else {return .invalid}
// We only want our string to contain a single email
// address, so if multiple matches were found, then we fail the validation and return nil
//6
guard let match = matches.first, !matches.isEmpty else {return .invalid}
// Verify that the found link points to an email address,
// and that it's range covers the whole input string:
//7
guard match.url?.scheme == "mailto", match.range == range else {return .invalid}
//8
return .valid
}
}
Let’s walk through the code and explain each line’s purpose to help you understand what’s going on:
-
guard !email.isEmpty else {return .empty }
: This line checks if the email address string is empty. If it is empty, the function returns .empty as the email state. -
let trimmedEmail = email.trimmingCharacters(in: .whitespacesAndNewlines)
: This line removes whitespace and newline characters from the beginning and end of the email address string using the trimmingCharacters(in:) method. -
let detector = try? NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue)
: This line creates an instance of NSDataDetector that can detect email addresses using NSTextCheckingResult.CheckingType.link.rawValue as the types. -
let range = NSRange(trimmedEmail.startIndex..<trimmedEmail.endIndex, in: trimmedEmail)
: This line creates an NSRange object that covers the entire length of the trimmed email address string. -
guard let matches = detector?.matches(in: trimmedEmail, options: [], range: range) else {return .invalid}
: This line uses the matches(in:options:range:) method to find all email address matches in the email address string. If no matches are found, the function returns .invalid. -
guard let match = matches.first, !matches.isEmpty else {return .invalid}
: This line checks that only one match was found in the email address string. If multiple matches were found, the function returns .invalid. -
guard match.url?.scheme == "mailto", match.range == range else {return .invalid}
: This line checks that the match found is indeed an email address by verifying that the URL scheme is “mailto” and that the match range covers the entire trimmed email address string. If this condition is not met, the function returns .invalid. -
return .valid
: This line indicates that the email address is valid since all the validation checks have passed, and the function returns .valid as the email state.
With the above in place, we can now use our Validator
object to validate any email address like this:
1
2
3
4
5
6
7
8
9
10
let validEmail = Validator.validate(email: "[email protected]")
switch validEmail {
case .valid:
// yay
case .empty:
// Ooops
case .invalid:
// not okie dokie
}
Conclusion
As you can see leveraging NSDataDetector class is a pretty neat way to quickly whip up a email validator. Hopefully you’ll consider this simple class next time you’re cooking up a email validator.
Hope you found this helpful ✌️