Validation de champ IOS - rapide et facile

imageLa validation des champs de saisie est peut-être la tâche la plus courante dans les applications mobiles. Chaque application qui prend la forme d'une autorisation et d'un enregistrement dispose également d'un certain nombre d'outils d'entrée de l'utilisateur, qui suscitent l'enthousiasme sadique et sophistiqué attendu des testeurs. Une communauté de développeurs avancés et techniquement compétents a appris à les gérer efficacement en incorporant des expressions régulières dans le code source.

En règle générale, lorsque vous devez vous limiter à l'enregistrement et à l'autorisation, vous n'avez pas besoin de faire des efforts particuliers pour atteindre votre objectif - le code est transféré d'un projet à un autre, presque sans aucun changement. Et, plus important encore, sans augmenter les heures de travail pour la maintenance de ce code. Mais le monde ne serait pas aussi simple sans les impulsions créatives des concepteurs UI / UX qui sont enclins, contrairement à la tradition établie, à la logique et au bon sens, à inventer de nouvelles façons d'interagir avec l'utilisateur final, en mettant sur le formulaire plusieurs, selon eux, les contrôles nécessaires, l'accessibilité qui dépend de l'ensemble cartésien de conditions pour vérifier la validité d'un grand nombre de champs de saisie et d'autres commandes de contrôle. Malheureusement, cette situation peut difficilement être qualifiée de rare.

L'irritation du développeur augmente proportionnellement à la fréquence à laquelle il doit violer les principes DRY: d'une part, la fierté professionnelle ne permet pas de compromis, et d'autre part, le code copier-coller est le moyen le plus efficace d'éviter un long cycle de test - le débogage. Le code copié monotone est beaucoup plus facile à entretenir qu'un "vélo" unique, vérifié idéologiquement. La recherche d'une alternative gaspille non seulement la créativité du développeur, mais ajoute également des dépendances au projet.

Dans le même temps, le SDK iOS présente des fonctionnalités extrêmement sous-estimées qui s'adaptent facilement à de nombreuses tâches liées non seulement à la validation - la programmation déclarative simplifie considérablement la vie du développeur. Bien sûr, l'auteur connaît l'implacable camp des amoureuxamis d'utiliser du code "non dilué", mais depuis que l'auteur de l'article a commencé à développer des interfaces professionnelles en développant une interface graphique pour MS DOS, il n'y a pas grand désir de perdre du temps à créer une autre classe parfaite, et si vous pouvez l'utiliser avec une valeur égale souris - la préférence sera donnée à la souris. En conséquence, il explique comment minimiser la quantité de code afin d'accélérer et de simplifier le processus de développement.

Avertissement:
, . . , — .

Une tâche minimale typique est la suivante:

il y a des champs de saisie de connexion et de mot de passe et un bouton d'autorisation. Il est nécessaire que le bouton d'autorisation change d'état (isEnable) selon ce qui est contenu dans les champs de saisie.

Une version légèrement plus avancée de cette tâche ressemble à ceci:

Nous avons les champs de saisie de l'e-mail, du mot de passe et du numéro de téléphone, ainsi que deux boutons - enregistrement et demande d'un code SMS pour le téléphone entré. Le bouton d'enregistrement ne doit être disponible que lorsque les données correctes sont entrées dans chacun des champs. Et le bouton de demande de code est lorsque le champ du numéro de téléphone est valide.

Solution typique- création de drapeaux interdépendants en combinant les instructions «if» et «switch» dans un contrôleur de vue. La difficulté augmentera avec le nombre de contrôles impliqués dans le processus. Une solution beaucoup plus avancée serait de créer une machine à états. La solution est excellente - mais prend du temps, en plus, avec un seuil d'entrée élevé - et ce n'est en aucun cas ce que je veux implémenter pour le développeur paresseux (AKA "vrai").

Lema à propos d'un développeur paresseux
, , . (« » ), , .

L'idée générale de la proposition est la suivante:

il existe une classe pour le champ de saisie et une classe pour le gestionnaire de validation. Le champ de saisie est hérité, comme prévu, de UITextField. Le gestionnaire de validation - hérité de UIControl, contient une collection d'éléments validés (il n'est pas du tout nécessaire d'être des descendants de UITextField) et implémente le modèle «observateur». De plus, il agit en tant que gestionnaire pour d'autres contrôles qui doivent changer leur état d'accessibilité lorsque l'état des éléments validés change. En d'autres termes, si le champ contient un e-mail non valide, le bouton d'enregistrement ne doit pas être disponible.



Les champs de saisie sont auto-validants - ils peuvent répondre à la question de savoir si les données qu'ils contiennent sont valides et changer leur état. Certains développeurs préfèrent utiliser une classe de validation distincte (et ses descendants) pour valider les champs d'entrée, mais cette pratique rend impossible de profiter pleinement de la portabilité déclarative et évolutive du code. Il est important pour nous que le champ de saisie ou tout autre contrôle validé supporte une interface simple du formulaire:

@objc protocol IValidatable : class {
    varisValid: Bool { get }
}

Pour ajouter une validation à votre projet, vous devez ajouter 4 fichiers à partir de l'exemple situé dans le référentiel GitHub .

  • La classe DSTextField implémente un champ de saisie avec une indication du processus de validation.
  • La classe DSValidationManager est un observateur qui fournit les fonctionnalités dont nous avons besoin.
  • Extension StringOptional + Utils - contient une seule méthode qui utilise une expression régulière pour vérifier si le texte de la ligne actuelle est valide.
  • UIView + Layer est juste un moyen pratique d'ajouter un cadre d'une largeur donnée à tout descendant d'UIView.

À strictement parler, si vous souhaitez implémenter la validation de la même manière pour tout autre contrôle, vous n'avez probablement besoin que de DSValidationManager. Le reste est utilisé uniquement pour votre commodité.

Le processus de validation est montré dans la vidéo (gif).


Comme vous pouvez le voir, avant le début de l'entrée, le champ est dans son état par défaut et la validation se produit lorsque les données sont entrées. Si vous quittez le champ avant la fin de la validation, une inscription apparaît sur le champ pour signaler l'erreur. Le bouton d'enregistrement ne sera pas disponible tant que les deux champs ne seront pas valides.

Une situation similaire se développe sur le deuxième écran (qui ne devient disponible qu'après activation de l'inscription). Mais sur le deuxième écran, la validation des champs et des boutons se produit indépendamment - le bouton d'enregistrement n'est disponible que si tous les champs ont échoué. Dans le même temps, le bouton d'envoi SMS est déjà disponible lorsque le champ avec le numéro de téléphone est valide.

Et maintenant l'astuce:L'ensemble du projet contient une seule classe pour le contrôleur de vue, la même que celle créée par Xcode lors de la création du projet à partir du modèle. Il est complètement vide. Mais même il n'est pas utilisé. Ce n'est pas difficile à vérifier avec l'aide du répartiteur.



Un lecteur attentif a remarqué qu'à la droite du répondeur se trouve un objet de la palette de composants Xcode standard. Pour cela, le paramètre Class est remplacé par la classe DSValidationManager . La même astuce a été effectuée pour les champs de saisie, à la seule différence que la classe DSTextField y est utilisée .

Maintenant, toute notre programmation se résume aux étapes simples suivantes:

  1. Associez la collection VerifiedControls de ValidationManager aux champs d'entrée.


  2. Associez la collection managedControls de ValidationManager au bouton que vous souhaitez gérer.


  3. Associez les champs de saisie dans la direction opposée à ValidationManager, ce qui en fait un délégué.

  4. Dans le champ de saisie, définissez une expression régulière pour la validation et un message d'erreur, ainsi que d'autres propriétés personnalisées et standard via le gestionnaire Xcode standard. En principe, tout sauf l'expression régulière peut être laissé "tel quel". Il vous suffit d'utiliser le clavier, puis de copier-coller la formule à partir de Tyrnet. Si le message d'erreur est manquant, il ne sera tout simplement pas affiché à l'écran.


Le code DSValidationManager est laid et primitif.

import UIKit

@objc protocol IValidationManager : class {
    func verificated()
}

@objc protocol IValidatable : class {
    var isValid: Bool { get }
}

class DSValidationManager : UIControl, IValidationManager
{
    @IBOutlet var verifiedControls: [UIControl]?
    @IBOutlet var managedControls: [UIControl]?

    private (set) var valid : Bool = false {
        didSet {
            self.sendActions(for: .valueChanged)
        }
    }
    
    overrideinit(frame: CGRect) {
        super.init(frame: frame)
        self.verificated()
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        self.verificated()
    }

    func verificated() {
        self.checkVerifiedControls()
        self.controlsAction()
    }

    private func checkVerifiedControls() {
        guard let list:[IValidatable] = self.verifiedControls?.filter({$0 isIValidatable}) as? [IValidatable]
            else { return }
        self.valid = list.filter({!$0.isValid}).count==0
    }
    
    private func controls Action() {
        guard let list = self.managedControls else { return }
        for item in list {
            item.isEnabled = self.valid
        }
    }
}

Comme vous pouvez le voir, il reçoit une notification d'une collection et agit sur une autre.

Un observateur en fait une propriété que nous n'avons même pas considérée dans cet exemple. Vous pouvez faire glisser la connexion de l'objet ValidationManager directement vers le contrôleur, y ajouter un événement similaire à un clic sur un bouton et traiter l'état du gestionnaire directement dans le contrôleur. Si vous le voulez vraiment. Cela peut être utile si vous devez non seulement verrouiller le contrôle, mais aussi effectuer un travail utile (par exemple, jouer une marche ou afficher AlertView).

De tout le code TextEdit, il convient de noter seulement que le champ doit prendre soin de définir correctement la propriété " isValid " et de tirer la méthode " Verified ()"De son délégué. Cependant, notez que le champ de saisie n'utilise pas une référence à l'objet gestionnaire, mais une collection. Cela vous permet de lier un champ de saisie à plusieurs gestionnaires.

En conséquence, la méthode devra être tirée par chacun des délégués.

    var isValid: Bool {
        return self.text.verification(self.expression, required:self.valueRequired)
    }

    @IBOutlet var validateDelegates: [IValidationManager]?

    private func handleDelegateAction(_ sender: UITextField) {
        guard let list = self.validateDelegates else { return }

        for validateDelegate in list {
            validateDelegate.verificated()
        }
    }

Maintenant, si vous devez créer un nouveau formulaire avec des champs de saisie, vous n'avez même pas besoin de définir des valeurs de propriétés personnalisées - il suffira de copier les attributs Runtime requis en bloc et de les appliquer à un nouveau champ (n'oubliez pas de simplement remplacer le nom de la classe).


De manière générale, l'astuce utilisant «l'objet» dans l'en-tête du formulaire peut être utilisée beaucoup plus largement que juste pour la validation de champ - elle convertit de manière réactive MVC en MVVM sans aucun Rx. Son utilisation la plus courante est le partage réseau et la notification des changements dans le modèle CoreData.

All Articles