Swift String Validating або проста валідація рядків на відповідність критеріям

Всім доброго часу доби. Сьогодні хочеться поговорити про проблему валідації рядків в IOS проектах. Думаю Ви як і я часто стикаєтеся з цим, коли треба перевірити, наприклад, поле пароля на відповідність декільком критеріям.

Наприклад:

— Довжина пароля більше 6 символів
— Мінімум одна цифра
— Букви верхнього і нижнього регістра

Найчастіше така вимога реалізовуються приблизно так:

func isPasswordCorrect(_ value:String) -> Bool {
// code for check length, number exist, uppercase and lowercase chars
}

Просто. Функція працює, пароль перевіряється. Всі задоволені.

Далі якщо нам треба перевірити полі email на коректність, ми також пишемо функцію, наприклад:

func isEmailCorrect(_ value:String) -> Bool {
// code for check length, number exist, uppercase and lowercase chars
}

І так далі.

По зростанню проекту функцій з такими перевірками стає все більше і більше. При створенні нового проекту нам треба починати все спочатку або копіювати ці функції з минулого проекту. Не дуже зручно. Один з варіантів вирішення під катом.

В один момент я зрозумів що пора вирішувати цю проблему.

Очевидним рішенням було написати свій Валідатор.

По правді, нічого нового я не вигадав, подібні валідатори вже існую і їх легко знайти на github. Але мета була написати саме свій, можливо краще ніж вже представлені

Головними завданнями були:

— Універсальний спосіб, викликати з будь-якого місця, тут же отримуємо результат
— Легка настройка, швидко зазначаємо критерії перевірки
— Масштабованість.

Першим кроком було визначити як ми буде створювати критерії. Для цього був створений протокол:

public protocol Criteriable {
/// debug string for debug description of problem
var debugErrorString : String {get}

/// Check if value conform to criteria
///
/// - Parameter value: value to be checked
/// - Returns: return true if conform
func isConform(to value:String) -> Bool
}

Для початку були реалізовані перевірки на Довжину, Регістри. Виглядає це приблизно так:

LowercaseLetterExistCriteria

public struct LowercaseLetterExistCriteria : Criteriable {
public var debugErrorString: String = debugMessage(LowercaseLetterExistCriteria.self, message:"no lowwercase char exist")

public init(){}

public func isСonform(to value: String) -> Bool {
for char in value.characters {
if String(char).lowercased() == String(char) && Int(String(char)) == nil {
return true
}
}
return false
}
}


NumberExistCriteria

public struct NumberExistCriteria : Criteriable {
public var debugErrorString: String = debugMessage(NumberExistCriteria.self, message:"no number char exist")

public init(){}

public func isСonform(to value: String) -> Bool {
for char in value.characters {
if let _ = Int(String(char)) {
return true
}
}
return false
}
}


UppercaseLetterExistCriteria

public struct UppercaseLetterExistCriteria : Criteriable {
public var debugErrorString: String = debugMessage(UppercaseLetterExistCriteria.self, message:"no uppercase char exist")

public init(){}

public func isСonform(to value: String) -> Bool {
for char in value.characters {
if String(char).uppercased() == String(char) && Int(String(char)) == nil {
return true
}
}
return false
}
}


Потім був реалізований сам валідатор:

StringValidator

/// Validator
public struct StringValidator {
/// predictions
public var criterias: [Criteriable]

///init
public init(_ criterias: [Criteriable]) {
self.criterias = criterias
}

/// validate redictors to comform
///
/// - Parameters:
/// - value: string than must be validate
/// - forceExit: if true -> stop process when first validation fail. else create array of fail criterias
/// - result: result of validating
public func isValide(_ value:String, forceExit:Bool, result:@escaping (ValidatorResult) -> ()) {
// validating code
}
}


Валідатор ініціалізується набором критеріїв, встановлюється прапор (збирати всі критерії, які не пройдені або обриватися при першому знайденому критерії) і передається рядок яка буде отвалидирована. Результатом отримуємо перераховується тип:


/// Validator result object
///
/// - valid: everething if ok
/// - notValid: find not valid criteria
/// - notValide: not valid array of criterias
public enum ValidatorResult {
case valid
case notValid(criteria:Criteriable)
case notValides(criterias:[Criteriable])
}

Також для масштабованості легко можна визначити власний критерій:


struct MyCustomCriteria : Criteriable {
var debugErrorString: String = debugMessage(MyCustomCriteria.self, message:"some debug message")
func isConform(to value: String) -> Bool {
/* some logic for check */
return false
}
}

В підсумку ми одержали потрібний нам функціонал, більше не треба плодити функції. Досить визначити набір критеріїв і перевіряти рядок за ним.

Так само був створений CocoaPod.

Висновок

В майбутньому є плани розширити функціонал валідатора на миттєву перевірку UITextField.
Так само є ідея розширити функціонал на перевірку не тільки рядків, а будь-яких об'єктів.

Сподіваюся, цей матеріал буде корисний.
Дякую за увагу.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.