Как правильно проверить email на валидность в Swift?
Лучший способ валидации email в Swift
Правильная валидация email — задача сложнее, чем кажется на первый взгляд. Простое наличие символов "@" и "." не гарантирует корректность адреса. Я покажу два подхода: с использованием NSDataDetector (рекомендуемый) и через регулярные выражения.
Метод 1: Использование NSDataDetector (рекомендуемый)
Начиная с iOS, Apple предоставляет мощный инструмент NSDataDetector, который умеет распознавать email адреса в тексте. Это самый надёжный способ, так как он использует встроенные механизмы системы:
extension String {
func isValidEmail() -> 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: self)
}
func isValidEmailUsingDetector() -> Bool {
let detector = try? NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue)
let matches = detector?.matches(in: self, options: [], range: NSRange(location: 0, length: self.utf16.count))
guard let match = matches?.first else { return false }
// Проверяем, что вся строка — это один email
return match.url?.scheme == "mailto" && match.range.length == self.utf16.count
}
}
// Пример использования:
let testEmails = ["user@example.com", "test@", "@test.com", "user@.com", "user@domain.co.uk"]
for email in testEmails {
print("\(email): \(email.isValidEmailUsingDetector())")
}
// Вывод:
// user@example.com: true
// test@: false
// @test.com: false
// user@.com: false
// user@domain.co.uk: true
Метод 2: Классическое регулярное выражение
Если вам нужен более гибкий контроль, используйте NSPredicate с регулярным выражением:
extension String {
func isValidEmailRegex() -> 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: self)
}
}
// Пример с дополнительной обработкой
func validateEmail(_ email: String) -> Bool {
let trimmedEmail = email.trimmingCharacters(in: .whitespacesAndNewlines)
guard !trimmedEmail.isEmpty else { return false }
return trimmedEmail.isValidEmailRegex()
}
Почему NSDataDetector лучше?
- Актуальность: Apple обновляет детектор вместе с ОС, он учитывает новые домены верхнего уровня (например, .online, .tech)
- Надёжность: Детектор проверяет не только формат, но и валидность схемы (mailto:)
- Простота: Не нужно разбираться в сложных регулярных выражениях
Важные замечания
Помните, что даже самая лучшая валидация формата не гарантирует, что email реально существует. Для полной проверки нужно отправлять письмо с подтверждением. Также не стоит делать валидацию слишком строгой — например, адреса вида "user+tag@example.com" или "user.name@domain.co.uk" абсолютно корректны по стандарту RFC 5322.
В большинстве случаев для iOS-приложения достаточно простой проверки через Если вы не хотите использовать регулярные выражения или детекторы, можно реализовать проверку, разбивая email на компоненты. Это даёт больше контроля над процессом: Этот метод проверяет базовые правила RFC 5321: локальная часть до 64 символов, домен должен содержать точку, TLD — от 2 символов. Он не такой всеобъемлющий, как регулярное выражение, но легко читается и модифицируется. Для большинства приложений этого более чем достаточно. Если вам нужно поддерживать интернациональные домены (IDN), обязательно используйте метод NSDataDetector или регулярное выражение, приведённое выше. Это отсечёт 99% опечаток и невалидных адресов.
<<Альтернативный подход: валидация компонентов вручную
func isValidEmailManual(_ email: String) -> Bool {
let trimmed = email.trimmingCharacters(in: .whitespacesAndNewlines)
// Проверяем наличие одного @
let parts = trimmed.split(separator: "@", maxSplits: 1)
guard parts.count == 2 else { return false }
let localPart = String(parts[0])
let domainPart = String(parts[1])
// Локальная часть не должна быть пустой
guard !localPart.isEmpty, localPart.count <= 64 else { return false }
// Домен должен содержать точку
guard domainPart.contains("."),
let lastDot = domainPart.lastIndex(of: ".") else { return false }
// TLD (часть после последней точки) должна быть длиной от 2 до 64 символов
let tld = domainPart[domainPart.index(after: lastDot)...]
guard tld.count >= 2, tld.count <= 64 else { return false }
return true
}
.precomposedStringWithCanonicalMapping для нормализации строки перед проверкой.
Ответов пока нет.
Войдите чтобы оставить ответ