J'ai récemment commencé à travailler sur un projet Swift (2) iOS et j'ai commencé à faire face à des situations dans lesquelles j'ai des formulaires avec de nombreux champs qui doivent être validés.
J'ai un arrière-plan principalement .Net et, avec Binding and Annotations, il est possible de mettre en œuvre la validation de formulaire proprement et sans maintenance, sur des formulaires de grande taille, avec quelques lignes de code.
Depuis que j'ai embrassé Swift, j’ai rencontré de nombreux exemples détaillant la validation de différentes manières, mais tout ce que j’ai rencontré jusqu’à présent semble être une tâche très laborieuse et très exigeante en termes de maintenance. la vue efficacement.
Par exemple:
Ma solution actuelle définit des extensions qui peuvent être vérifiées dans une fonction, règle par règle, lors de la validation de l'entrée de champ. Toutefois, j'estime qu'il doit exister une solution plus évolutive à ce problème.
Quelles approches peuvent être prises lors de la validation de formulaires avec potentiellement de nombreux intrants, qui favorisent la maintenabilité?
Par exemple sake, nous pourrions discuter d’un formulaire hypothétique avec:
Bien sûr, je ne cherche pas une implémentation qui réponde littéralement à la liste ci-dessus, mais une méthode ou une approche pouvant être mise à l’échelle entre ces types de scénarios.
"Ugh, formes"
-Sir Albert Einstein
Oui, créer un formulaire évolutif dans iOS peut être un travail difficile et monotone. C'est pourquoi j'ai une classe de base appelée FormViewController
qui expose quelques méthodes de validation courantes et quelques méthodes que vous pouvez utiliser pour ajouter une validation personnalisée.
Maintenant, le code suivant pourrait être très long, et je ne vais pas expliquer chaque ligne. Revenez sous la forme de commentaires, si vous avez des doutes.
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
}
SwiftValidator est une autre option qui semble bonne jusqu'à présent. C'est un projet actif et il ne m'a fallu que quelques minutes pour le configurer.
Il y a EGFormValidator library écrit dans Swift 3. Il est flexible et facile à utiliser.