Khi phát triển app, thường phải validate nhiều trường input (đăng ký, đăng nhập...). Viết validation riêng cho từng field lặp code, dễ sai sót. Giải pháp: **Generic Validation Library** - viết một lần, dùng cho mọi field.
Ưu điểm
- Code tái sử dụng (reusable)
- Giảm lỗi con người
- Setup nhanh, hook vào code dễ dàng
- Sửa một chỗ → ảnh hưởng toàn bộ
- Dễ maintain, test
Triển khai Validation Library
Bước 1: Tạo class Validation
File → New → Swift File → Validation.swift
import Foundation
class Validation {
// MARK: - Name Validation
/// Tên: 3-18 ký tự, chỉ chữ/c số/_ (tuỳ chỉnh regex theo nhu cầu)
func validateName(_ name: String) -> Bool {
let nameRegex = "^\\w{3,18}$"
let trimmed = name.trimmingCharacters(in: .whitespaces)
let predicate = NSPredicate(format: "SELF MATCHES %@", nameRegex)
return predicate.evaluate(with: trimmed)
}
// MARK: - Phone Validation (Vietnam format)
/// SĐT VN: 10-11 số, bắt đầu bằng 0 + (3|5|7|8|9)
func validatePhoneNumber(_ phoneNumber: String) -> Bool {
let phoneRegex = "^(0[3|5|7|8|9])+([0-9]{8,9})$"
let trimmed = phoneNumber.trimmingCharacters(in: .whitespaces)
let predicate = NSPredicate(format: "SELF MATCHES %@", phoneRegex)
return predicate.evaluate(with: trimmed)
}
// MARK: - Email Validation
func validateEmail(_ email: String) -> Bool {
let emailRegex = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
let trimmed = email.trimmingCharacters(in: .whitespaces)
let predicate = NSPredicate(format: "SELF MATCHES %@", emailRegex)
return predicate.evaluate(with: trimmed)
}
// MARK: - Password Validation
/// Tối thiểu 8 ký tự, ít nhất 1 chữ cái và 1 số
func validatePassword(_ password: String) -> Bool {
let passRegex = "^(?=.*[A-Za-z])(?=.*\\d)[A-Za-z\\d]{8,}$"
let trimmed = password.trimmingCharacters(in: .whitespaces)
let predicate = NSPredicate(format: "SELF MATCHES %@", passRegex)
return predicate.evaluate(with: trimmed)
}
// MARK: - Generic Validation
/// Validate bất kỳ field nào với regex tùy chỉnh
func validate(_ text: String, regex: String) -> Bool {
let trimmed = text.trimmingCharacters(in: .whitespaces)
let predicate = NSPredicate(format: "SELF MATCHES %@", regex)
return predicate.evaluate(with: trimmed)
}
}
Bước 2: Sử dụng trong ViewController
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var nameTextField: UITextField!
@IBOutlet weak var emailTextField: UITextField!
@IBOutlet weak var passwordTextField: UITextField!
@IBOutlet weak var phoneTextField: UITextField!
let validation = Validation()
@IBAction func validateButtonTapped(_ sender: Any) {
// 1. Unwrap an toàn
guard let name = nameTextField.text,
let email = emailTextField.text,
let password = passwordTextField.text,
let phone = phoneTextField.text else {
print("Có trường trống")
return
}
// 2. Validate từng field
if !validation.validateName(name) {
showAlert("Tên không hợp lệ (3-18 ký tự)")
return
}
if !validation.validateEmail(email) {
showAlert("Email không đúng định dạng")
return
}
if !validation.validatePassword(password) {
showAlert("Mật khẩu: tối thiểu 8 ký tự, có chữ và số")
return
}
if !validation.validatePhoneNumber(phone) {
showAlert("Số điện thoại VN không hợp lệ")
return
}
// 3. Tất cả hợp lệ
showAlert("✅ Tất cả trường đều hợp lệ!")
}
func showAlert(_ message: String) {
let alert = UIAlertController(title: "Thông báo", message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default))
present(alert, animated: true)
}
}
Mở rộng: Validation với Error Messages chi tiết
struct ValidationResult {
let isValid: Bool
let errorMessage: String?
}
class Validator {
static func validate(_ text: String?, rules: [ValidationRule]) -> ValidationResult {
guard let text = text, !text.isEmpty else {
return ValidationResult(isValid: false, errorMessage: "Trường này không được để trống")
}
for rule in rules {
if !rule.check(text) {
return ValidationResult(isValid: false, errorMessage: rule.errorMessage)
}
}
return ValidationResult(isValid: true, errorMessage: nil)
}
}
struct ValidationRule {
let check: (String) -> Bool
let errorMessage: String
// Predefined rules
static let required = ValidationRule(
check: { !$0.trimmingCharacters(in: .whitespaces).isEmpty },
errorMessage: "Bắt buộc nhập"
)
static let email = ValidationRule(
check: { NSPredicate(format: "SELF MATCHES %@", "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}").evaluate(with: $0) },
errorMessage: "Email không hợp lệ"
)
static func minLength(_ length: Int) -> ValidationRule {
ValidationRule(
check: { $0.count >= length },
errorMessage: "Tối thiểu \(length) ký tự"
)
}
static func regex(_ pattern: String, message: String) -> ValidationRule {
ValidationRule(
check: { NSPredicate(format: "SELF MATCHES %@", pattern).evaluate(with: $0) },
errorMessage: message
)
}
}
// Usage
let result = Validator.validate(emailTextField.text, rules: [.required, .email])
if !result.isValid { showError(result.errorMessage!) }
Thư viện Validation phổ biến (Production)
- Validator (Swift):
pod 'Validator'- Declarative, chainable - RxSwift + RxCocoa: Reactive validation với
BehaviorRelay - Combine (iOS 13+): Built-in reactive validation
Kết luận
Generic validation giúp code sạch, maintainable, ít bug. Bắt đầu với class Validation đơn giản, sau đó có thể mở rộng sang struct-based rule system hoặc dùng thư viện chuyên biệt cho project lớn.