Как стать автором
Обновить

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

Время на прочтение3 мин
Количество просмотров8.5K
Всем доброго времени суток. Сегодня хочется поговорить про проблему валидации строк в 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 lowercase char exists")
    
    public init(){}

    public func isСonform(to value: String)  -> Bool {
        for char in value.characters {
            if char.isLowercase() == true {
                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 {
        let regExptest = NSPredicate(format: "SELF MATCHES %@", ".*[0-9]+.*")
        return regExptest.evaluate(with: value)
    }
}


UppercaseLetterExistCriteria

public struct UppercaseLetterExistCriteria : Criteriable {
    public var debugErrorString: String = debugMessage(UppercaseLetterExistCriteria.self, message:"no uppercase char exists")
    
    public init(){}
    
    public func isСonform(to value: String) -> Bool {
        for char in value.characters {
            if char.isUppercase() == true {
                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.
Так же есть идея расширить функционал на проверку не только строк, а любых обьектов.

Надеюсь данный материал будет полезен.
Спасибо за внимание.
Теги:
Хабы:
+10
Комментарии7

Публикации

Истории

Работа

Swift разработчик
30 вакансий
iOS разработчик
22 вакансии

Ближайшие события