Swift 中的表单验证

Posted

技术标签:

【中文标题】Swift 中的表单验证【英文标题】:Form validation in Swift 【发布时间】:2016-07-18 03:37:18 【问题描述】:

我最近开始了一个 Swift (2) ios 项目,并开始面临我的表单包含许多需要验证的字段的情况。

我的背景主要是 .Net,通过绑定和注释,可以在大型表单上以低维护的方式干净利落地实现表单验证,并且只需要几行代码。

自从使用 Swift 以来,我遇到了许多以各种方式详细说明验证的示例,但到目前为止,我遇到的所有事情似乎都非常耗费人力且维护成本很高,或者解释集中在进行验证本身而不是连接验证过程模型和视图有效。

例如:

Validating TextField Validate TextField as Double using extensions Validating email addresses

我当前的解决方案定义了可以在验证字段输入时在函数中逐条检查的扩展,但是我觉得必须有一个更具可扩展性的解决方案来解决这个问题。

在验证可能有很多输入的表单时,可以采取哪些方法来提高可维护性?

例如,我们可以讨论一个假设的形式:

要求输入数字的文本字段 (Int)。 要求输入十进制数的文本字段(双精度)。 请求输入数字的文本字段,匹配特定规则(例如,是素数)(双)。 要求输入长度为 kL 的字符串的文本字段。 要求输入符合某些自定义规则的字符串的文本字段。

当然,我不是在寻找真正满足上面列表的实现,而是在这些类型的场景中可扩展的方法或方法。

【问题讨论】:

【参考方案1】:

“呃,表格”

-阿尔伯特·爱因斯坦爵士

是的,在 iOS 中构建可扩展的表单可能是一项艰巨而单调的工作。这就是为什么我有一个名为 FormViewController 的基类,它公开了一些常见的验证方法和一些可用于添加自定义验证的方法。

现在,下面的代码可能很长,我不会解释每一行。如果您有任何疑问,请以 cmets 的形式恢复。

import UIKit

typealias TextFieldPredicate = ( (String) -> (Bool) )

class FormViewController : UIViewController 

    var activeTextField : UITextField!

    private var mandatoryFields  = [UITextField]()
    private var emptyErrorMessages = [String]()

    private var emailFields = [UITextField]()
    private var emailErrorMessages = [String]()

    private var specialValidationFields = [UITextField]()
    private var specialValidationMethods = [TextFieldPredicate]()
    private var specialValidationErrorMessages = [String]()

    override func viewDidLoad() 
        super.viewDidLoad()

        // Do any additional setup after loading the view.
    

    override func viewWillAppear(animated: Bool) 
        super.viewWillAppear(animated)
        registerForNotifications()
    

    override func viewWillDisappear(animated: Bool) 
        super.viewWillDisappear(animated)
        NSNotificationCenter.defaultCenter().removeObserver(self)
    

    override func didReceiveMemoryWarning() 
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    


    private func registerForNotifications() 
        NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(FormViewController.keyboardWillShow(_:)), name:UIKeyboardWillShowNotification, object: nil);
        NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(FormViewController.keyboardWillHide(_:)), name:UIKeyboardWillHideNotification, object: nil);
    

    func keyboardWillShow(notification:NSNotification?) 
        let keyboardSize = notification?.userInfo![UIKeyboardFrameBeginUserInfoKey]!.CGRectValue.size
        self.view.frame.origin.y = 0
        let keyboardYPosition = self.view.frame.size.height - keyboardSize!.height
        if keyboardYPosition < self.activeTextField!.frame.origin.y 
            UIView.animateWithDuration(GlobalConstants.AnimationTimes.SHORT)  () -> Void in
                self.view.frame.origin.y = self.view.frame.origin.y - keyboardSize!.height + 30
            
        
    

    func keyboardWillHide(notification:NSNotification?) 
        UIView.animateWithDuration(GlobalConstants.AnimationTimes.SHORT)  () -> Void in
            self.view.frame.origin.y = 0
        
    

    func validateEmailForFields(emailTextFields:[UITextField]) -> [Bool] 
        var validatedBits = [Bool]()
        for emailTextField in emailTextFields 
            if let text = emailTextField.text where !text.isValidEmail() 
                emailTextField.shakeViewForTimes(WelcomeViewController.ERROR_SHAKE_COUNT)
                validatedBits.append(false)
             else 
                validatedBits.append(true)
            
        
        return validatedBits
    

    func validateSpecialTextFields(specialTextFields:[UITextField]) -> [Bool] 
        var validatedBits = [Bool]()
        for specialTextField in specialTextFields 
            let specialValidationMethod = self.specialValidationMethods[ specialValidationFields.indexOf(specialTextField)!]
            validatedBits.append(specialValidationMethod(specialTextField.text!))
        
        return validatedBits
    

    func validateEmptyFields(textFields : [UITextField]) -> [Bool] 
        var validatedBits = [Bool]()
        for textField in textFields 
            if let text = textField.text where text.isEmpty 
                textField.shakeViewForTimes(WelcomeViewController.ERROR_SHAKE_COUNT)
                validatedBits.append(false)
             else 
                validatedBits.append(true)
            
        
        return validatedBits
    

    func addMandatoryField(textField : UITextField, message : String) 
        self.mandatoryFields.append(textField)
        self.emptyErrorMessages.append(message)
    

    func addEmailField(textField : UITextField , message : String) 
        textField.keyboardType = .EmailAddress
        self.emailFields.append(textField)
        self.emailErrorMessages.append(message)
    

    func addSpecialValidationField(textField : UITextField , message : String, textFieldPredicate : TextFieldPredicate) 
        self.specialValidationErrorMessages.append(message)
        self.specialValidationMethods.append(textFieldPredicate)
        self.specialValidationFields.append(textField)
    

    func errorMessageForEmptyTextField(textField : UITextField) throws -> String  
        if self.mandatoryFields.contains(textField) 
            return self.emptyErrorMessages[self.mandatoryFields.indexOf(textField)!]
         else 
            throw ValidationError.NonMandatoryTextField
        
    

    func errorMessageForMultipleEmptyErrors() -> String 
        return "Fields cannot be empty"
    

    func errorMessageForMutipleEmailError() -> String 
        return "Invalid email addresses"
    

    @IBAction func didTapFinishButton(sender:AnyObject?) 
        if let errorMessage = self.errorMessageAfterPerformingValidation() 
            self.showVisualFeedbackWithErrorMessage(errorMessage)
            return
        
        self.didCompleteValidationSuccessfully()
    

    func showVisualFeedbackWithErrorMessage(errorMessage : String) 
        fatalError("Implement this method")
    

    func didCompleteValidationSuccessfully() 

    

    func errorMessageAfterPerformingValidation() -> String? 
        if let errorMessage = self.errorMessageAfterPerformingEmptyValidations() 
            return errorMessage
        
        if let errorMessage = self.errorMessageAfterPerformingEmailValidations() 
            return errorMessage
        
        if let errorMessage = self.errorMessageAfterPerformingSpecialValidations() 
            return errorMessage
        
        return nil
    

    private func errorMessageAfterPerformingEmptyValidations() -> String? 
        let emptyValidationBits = self.performEmptyValidations()
        var index = 0
        var errorCount = 0
        var errorMessage : String?
        for validation in emptyValidationBits 
            if !validation 
                errorMessage = self.emptyErrorMessages[index]
                errorCount += 1
            
            if errorCount > 1 
                return self.errorMessageForMultipleEmptyErrors()
            
            index = index + 1
        
        return errorMessage
    

    private func errorMessageAfterPerformingEmailValidations() -> String? 
        let emptyValidationBits = self.performEmailValidations()
        var index = 0
        var errorCount = 0
        var errorMessage : String?
        for validation in emptyValidationBits 
            if !validation 
                errorMessage = self.emailErrorMessages[index]
                errorCount += 1
            
            if errorCount > 1 
                return self.errorMessageForMutipleEmailError()
            
            index = index + 1
        
        return errorMessage
    

    private func errorMessageAfterPerformingSpecialValidations() -> String? 
        let emptyValidationBits = self.performSpecialValidations()
        var index = 0
        for validation in emptyValidationBits 
            if !validation 
                return self.specialValidationErrorMessages[index]
            
            index = index + 1
        
        return nil
    

    func performEqualValidationsForTextField(textField : UITextField, anotherTextField : UITextField) -> Bool 
        return textField.text! == anotherTextField.text!
    


    private func performEmptyValidations() -> [Bool] 
        return validateEmptyFields(self.mandatoryFields)
    
    private func performEmailValidations() -> [Bool] 
        return validateEmailForFields(self.emailFields)
    
    private func performSpecialValidations() -> [Bool] 
        return validateSpecialTextFields(self.specialValidationFields)
    




extension FormViewController : UITextFieldDelegate 
    func textFieldDidBeginEditing(textField: UITextField) 
        self.activeTextField = textField
    
    func textFieldDidEndEditing(textField: UITextField) 
        self.activeTextField = nil
    


enum ValidationError : ErrorType 
    case NonMandatoryTextField

【讨论】:

您的解决方案似乎是我应该采取的下一个合乎逻辑的步骤。感谢您的贡献。我特别喜欢它看起来非常独立,并展示了一些我会忽略的改进。 谢谢,有一些依赖项(如shakeViewSHAKE_COUNT)。我应该很快重构它。【参考方案2】:

EGFormValidator 库是用 Swift 3 编写的。它灵活且易于使用。

【讨论】:

【参考方案3】:

目前看来不错的另一个选择是 SwiftValidator。 这是一个活跃的项目,我只需要几分钟就可以在我的项目中进行设置。

https://github.com/jpotts18/SwiftValidator

【讨论】:

【参考方案4】:

尽管已经晚了 3 年,但我还是把它放在这里,供任何想要灵活和可扩展选项的人使用。据我所知,这将完全适合作为问题的答案。

请查看ATGValidator。

我没有在此处添加任何代码,因为框架链接中的自述文件包含大量相同的文档。

【讨论】:

以上是关于Swift 中的表单验证的主要内容,如果未能解决你的问题,请参考以下文章

swift4中的UITextfield验证 - 动态单元格

什么是表单验证?

js怎么验证表单?

表单验证 - jQuery 验证是不是足够以及如何巧妙地验证 PHP 中的数据?

JS表单验证

AngularJS中的表单验证