Валидация данных в Swift (iOS/macOS): инструменты и практика

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Валидация данных в Swift (iOS/macOS): обзор инструментов и библиотек

Валидация данных — один из ключевых аспектов разработки качественных мобильных и десктопных приложений на Swift. Она позволяет гарантировать, что вводимые пользователем данные соответствуют ожидаемому формату и бизнес-правилам. В этой статье мы рассмотрим основные подходы, встроенные механизмы и популярные библиотеки для валидации в экосистеме Apple.

Почему важна валидация?

Без валидации приложение рискует получить некорректные данные, что может привести к:

  • Ошибкам в работе серверной части
  • Неожиданным сбоям (crash) на стороне клиента
  • Уязвимостям безопасности (SQL-инъекции, XSS)
  • Плохому пользовательскому опыту

Встроенные возможности Swift и Foundation

Swift и фреймворк Foundation предоставляют базовые, но мощные инструменты для валидации.

1. Проверка с помощью NSPredicate

NSPredicate — мощный инструмент для фильтрации и проверки данных. Он поддерживает регулярные выражения, что делает его удобным для валидации email, телефонов и других строк.

import Foundation

func isValidEmail(_ email: String) -> Bool {
    let emailRegEx = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
    let predicate = NSPredicate(format:"SELF MATCHES %@", emailRegEx)
    return predicate.evaluate(with: email)
}

// Пример использования
let testEmail = "user@example.com"
print(isValidEmail(testEmail)) // true

2. Валидация через NSRegularExpression

Для более сложных сценариев удобно использовать NSRegularExpression.

func isValidPassword(_ password: String) -> Bool {
    // Минимум 8 символов, одна заглавная, одна строчная, одна цифра
    let passwordRegex = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)[a-zA-Z\\d]{8,}$"
    do {
        let regex = try NSRegularExpression(pattern: passwordRegex)
        let range = NSRange(location: 0, length: password.utf16.count)
        return regex.firstMatch(in: password, options: [], range: range) != nil
    } catch {
        return false
    }
}

print(isValidPassword("Swift1234")) // true
print(isValidPassword("swift123"))  // false (нет заглавной)

Популярные библиотеки для валидации

Хотя встроенные средства достаточны для простых задач, для сложных форм с множеством правил удобнее использовать специализированные библиотеки.

1. Validator (от Adam Waite)

Одна из самых популярных библиотек для валидации строк в Swift. Поддерживает цепочки правил (chain validation) и кастомные валидаторы.

// Установка через SPM: .package(url: "https://github.com/adamwaite/Validator.git", from: "3.0.0")
import Validator

let emailRule = ValidationRulePattern(pattern: EmailValidationPattern.standard, error: "Некорректный email")
let nameRule = ValidationRuleLength(min: 2, max: 50, error: "Имя должно быть от 2 до 50 символов")

let email = "test@mail.ru"
let result = email.validate(rule: emailRule)
switch result {
case .valid:
    print("Email корректен")
case .invalid(let errors):
    print(errors.first?.message ?? "Ошибка")
}

2. SwiftValidators (от gkaimakas)

Легковесная библиотека с декларативным синтаксисом. Отлично подходит для проверки полей в реальном времени.

import SwiftValidators

let phone = "+7 (999) 123-45-67"
if Validator.isPhoneNumber().trim().validate(phone) {
    print("Телефон валидный")
} else {
    print("Неверный формат")
}

Валидация в SwiftUI с помощью Combine

Современные приложения на SwiftUI часто используют Combine для реактивной валидации. Это позволяет показывать ошибки мгновенно, по мере ввода.

import SwiftUI
import Combine

class RegistrationViewModel: ObservableObject {
    @Published var email = ""
    @Published var password = ""
    @Published var isFormValid = false
    
    private var cancellables = Set<AnyCancellable>()
    
    init() {
        // Реактивная проверка
        Publishers.CombineLatest($email, $password)
            .map { email, password in
                let validEmail = email.contains("@") && email.contains(".")
                let validPassword = password.count >= 8
                return validEmail && validPassword
            }
            .assign(to: &$isFormValid)
    }
}

struct RegistrationView: View {
    @StateObject private var viewModel = RegistrationViewModel()
    
    var body: some View {
        Form {
            TextField("Email", text: $viewModel.email)
                .autocapitalization(.none)
            SecureField("                    

Рекомендаций