One time I ordered a bike online. Whoops.
The bike was un-rideable by the time it finally arrived.
The wheels wobbled and rubbed against the brakes. The sound was awful. Without a spoke wrench, it was impossible to adjust the wheel myself. I pushed the bike to the nearest bike shop (0.7 miles, but who’s counting?), and got an arm workout in the process. If I’d tried to re-invent the wheel, it could not have gone more poorly.
This is a story about decidedly not re-inventing the wheel.
Problem
On our iOS app, we wanted to give users quick feedback when they entered a malformed email address on the sign-up form. We have server-side validation, but it shouldn’t require a round-trip to the server to know that “foo~&(&)(@bar.com” is not a valid email address.
We thought about writing a regex for matching email address, and quickly ran into questions. Would it validate only for structurally correct email addresses? What is the correct regex for matching email addresses? Should the matcher also match top-level domains (TLDs)? What about common typos?
Writing a regex felt like re-inventing the wheel. As confident as we were in our engineering skills, we were even more confident that we’d screw this up somehow. We’d forget a bracket or comma, or maybe add an extra question mark.
Surely someone brighter than us had already solved this problem, right?! So, we turned to Google.
Solution
We found this Stack Overflow answer, which introduced us to NSDataDetector
. This is a subclass of NSRegularExpression
, and is designed “to match natural language text for predefined data patterns.” It includes facilities for matching email addresses!
We wrote a small class to abstract this away. It has 1 method. The method takes in a text field, and returns the email address that’s been entered into the text field. If there is trailing or leading whitespace, it trims it. If there is more than 1 email address in the text field, it returns nil. If there are 0 email addresses in the text field, it also returns nil. We use this validator in several places in the Stitch Fix iOS app, and are very pleased that we did not reinvent the wheel.
//Swift 3
class EmailTextFieldValidator {
func validate(field: UITextField) -> String? {
guard let trimmedText = field.text?.trimmingCharacters(in: .whitespacesAndNewlines) else {
return nil
}
guard let dataDetector = try? NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue) else {
return nil
}
let range = NSMakeRange(0, NSString(string: trimmedText).length)
let allMatches = dataDetector.matches(in: trimmedText,
options: [],
range: range)
if allMatches.count == 1,
allMatches.first?.url?.absoluteString.contains("mailto:") == true
{
return trimmedText
}
return nil
}
}
Conclusion
In cycling, a wheel that doesn’t wobble is considered to be “true”. We’re happy with the email validation solution we’ve found here - it’s also true, and did not involve re-inventing the wheel.
Feel free to use this validation in your own apps. You might consider extending it to include TLD validation, or correct for common typos (i.e - changing “.con” to “.com”). If this was useful for you, our iOS team would love to hear about it - please email rachel at stitchfix.com to let us know!