Как правильно проверить email на валидность в Swift?
Проверка email с помощью регулярного выражения в Swift
Для валидации email в Swift лучше всего использовать NSRegularExpression и NSPredicate. Вот проверенный и надёжный метод, который покрывает большинство реальных случаев.
Основное решение
Создадим расширение для String с методом проверки email:
extension String {
func isValidEmail() -> Bool {
let emailRegex = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
let emailPredicate = NSPredicate(format: "SELF MATCHES %@", emailRegex)
return emailPredicate.evaluate(with: self)
}
}
// Пример использования:
let testEmail = "user@example.com"
if testEmail.isValidEmail() {
print("Email корректен")
} else {
print("Email невалиден")
}
Разбор регулярного выражения
[A-Z0-9a-z._%+-]+— локальная часть (до @). Допускаются буквы, цифры, точки, подчёркивания, проценты, плюсы и минусы. Один или более символов.@— обязательный символ "собаки".[A-Za-z0-9.-]+— доменное имя (после @). Буквы, цифры, точки и дефисы.\\.— экранированная точка перед доменом верхнего уровня.[A-Za-z]{2,64}— домен верхнего уровня (TLD). Только буквы, от 2 до 64 символов (например, .com, .ru, .online).
Более строгая версия (рекомендуется)
Для продакшена лучше использовать более точное выражение, которое отсекает большинство нестандартных случаев:
func isValidEmailStrict() -> Bool {
let emailRegex = "(?:[\\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])+)\\])"
let emailPredicate = NSPredicate(format: "SELF MATCHES %@", emailRegex)
return emailPredicate.evaluate(with: self)
}
Важные замечания
- Не пытайтесь валидировать email на 100% — RFC 5322 допускает очень странные форматы (например, "user@[192.168.1.1]" или "user+tag@example.com"). Регулярное выражение, покрывающее всё, будет огромным и медленным.
- Лучший способ проверки — отправить письмо с кодом подтверждения. Валидация на стороне клиента нужна только для отсеивания очевидных ошибок (пропущен @, нет домена).
- Юникодные символы — современные email могут содержать символы национальных алфавитов (например, "пользователь@пример.рф"). Используйте
\\p{L}для поддержки любых букв.
Обработка ошибок в UI
Пример использования с текстовым полем:
func validateEmailField(_ textField: UITextField) {
guard let email = textField.text, !email.isEmpty else {
showError("Email не может быть пустым")
return
}
if email.isValidEmail() {
hideError()
} else {
showError("Введите корректный email (например, name@domain.com)")
}
}
func showError(_ message: String) {
// Показываем UILabel с ошибкой
errorLabel.text = message
errorLabel.isHidden = false
}
func hideError() {
errorLabel.isHidden = true
}
Этот подход даёт хороший баланс между точностью и производительностью для большинства iOS-приложений.
<<Альтернативный подход: проверка без регулярных выражений
Если вы не хотите использовать сложные регулярные выражения, можно написать более понятную, хотя и менее строгую проверку. Этот метод подойдёт для простых случаев или для обучения:
func isValidEmailSimple(_ email: String) -> Bool {
// 1. Убираем пробелы по краям
let trimmed = email.trimmingCharacters(in: .whitespacesAndNewlines)
// 2. Проверяем наличие @
guard let atIndex = trimmed.firstIndex(of: "@") else { return false }
// 3. Локальная часть (до @) не должна быть пустой
let localPart = trimmed[.. 0 else { return false }
// 4. Доменная часть (после @)
let domainPart = trimmed[trimmed.index(after: atIndex)...]
guard domainPart.count > 0 else { return false }
// 5. В домене должна быть хотя бы одна точка
guard domainPart.contains(".") else { return false }
// 6. Домен не должен начинаться или заканчиваться точкой
guard domainPart.first != ".", domainPart.last != "." else { return false }
// 7. Проверка на двойные точки
guard !localPart.contains(".."), !domainPart.contains("..") else { return false }
return true
}
Плюсы и минусы
- Плюсы: Код легко читается и модифицируется. Не нужно разбираться в регулярных выражениях.
- Минусы: Пропускает некоторые невалидные адреса (например, "a@b.c" без домена верхнего уровня). Не поддерживает сложные форматы вроде кавычек или IP-адресов.
Этот вариант хорош для прототипов или когда вам нужна базовая проверка без установки дополнительных зависимостей. Для продакшена рекомендую использовать первый вариант с NSPredicate.
Ответов пока нет.
Войдите чтобы оставить ответ