Unlimited Plugins, WordPress themes, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Code
  2. iOS SDK
Code

La bonne façon de partager l'état entre les contrôleurs de vue Swift

by
Difficulty:IntermediateLength:LongLanguages:

French (Français) translation by New Lune (you can also view the original English article)

Final product image
What You'll Be Creating

Il y a quelques années, lorsque j'étais encore employé dans un cabinet de conseil mobile, j'ai travaillé sur une application pour une grande banque d'investissement. Les grandes entreprises, en particulier les banques, ont généralement des processus en place pour s'assurer que leur logiciel est sécurisé, robuste et durable.

Une partie de ce processus impliquait l'envoi du code de l'application que j'ai écrite à un tiers pour examen. Cela ne m'a pas dérangé, car je pensais que mon code était impeccable et que la société de révision disait la même chose.

Quand leur réponse est revenue, le verdict était différent de ce que je pensais. Bien qu'ils aient dit que la qualité du code n'était pas mauvaise, ils ont souligné le fait que le code était difficile à maintenir et à tester (les tests unitaires n'étaient pas très populaires dans le développement iOS à l'époque).

J'ai rejeté leur jugement, pensant que mon code était génial et qu'il n'y avait aucun moyen d'améliorer. Ils ne doivent tout simplement pas le comprendre!

J'avais l'habileté du développeur typique: nous pensons souvent que ce que nous faisons est génial et d'autres ne l'ont pas compris.

En recul, j'ai eu tort. Pas beaucoup plus tard, j'ai commencé à lire des pratiques exemplaires. À partir de ce moment-là, les problèmes de mon code ont commencé à se révéler comme un pouce endolori. Je me suis rendu compte que, comme beaucoup de développeurs iOS, j'avais succombé à certains pièges classiques des mauvaises pratiques de codage.

Ce que la plupart des développeurs iOS se méfient

L'une des mauvaises pratiques de développement iOS les plus courantes survient lorsqu'on passe l'état entre les contrôleurs de vue d'une application. Je suis tombé dans ce piège dans le passé.

La propagation de l'état à travers les contrôleurs de vue est essentielle dans toute application iOS. Lorsque vos utilisateurs naviguent à travers les écrans de votre application et qu'ils interagissent avec celui-ci, vous devez conserver un état global qui suit toutes les modifications apportées par l'utilisateur aux données.

Et c'est là que la plupart des développeurs iOS atteignent la solution évidente mais incorrecte: le modèle singleton.

Le modèle singleton est très rapide à mettre en œuvre, en particulier dans Swift, et ça marche bien. Il suffit d'ajouter une variable statique à une classe pour conserver une instance partagée de la classe elle-même et vous avez terminé.

Il est alors facile d'accéder à cette instance partagée de n'importe où dans votre code:

Pour cette raison, de nombreux développeurs pensent avoir trouvé la meilleure solution au problème de la propagation de l'état. Mais ils ont tort.

Le modèle singleton est réellement considéré comme un anti-modèle. Il y a eu beaucoup de discussions dans la communauté du développement. Par exemple, voir cette question de débordement de pile.

En un mot, les singletons créent ces problèmes:

  • Ils présentent beaucoup de dépendances dans vos cours, ce qui rend plus difficile de les changer à l'avenir.
  • Ils rendent l'état global accessible à n'importe quelle partie de votre code. Cela peut créer des interactions complexes difficiles à suivre et provoquer de nombreux bugs inattendus.
  • Ils rendent vos classes très difficiles à tester, car vous ne pouvez pas les séparer d'un singleton facilement.

À ce stade, certains développeurs pensent: "Ah, j'ai une meilleure solution. J'utiliserai plutôt AppDelegate".

Le problème est que la classe AppDelegate dans les applications iOS est accessible via l'instance partagée UIApplication:

Mais l'instance partagée d'UIApplication est elle-même un singleton. Vous n'avez donc rien résolu!

La solution à ce problème est l'injection de dépendance. L'injection de dépendance signifie qu'une classe ne récupère pas ou crée ses propres dépendances, mais elle les reçoit de l'extérieur.

Pour voir comment utiliser l'injection de dépendance dans les applications iOS et comment elle permet le partage d'état, nous devons d'abord revoir l'un des modèles architecturaux fondamentaux des applications iOS: le modèle Model-View-Controller.

Extension du modèle MVC

Le modèle MVC, en un mot, indique qu'il existe trois couches dans l'architecture d'une application iOS:

  • La couche modèle représente les données d'une application.
  • La couche de vue affiche des informations sur l'écran et permet l'interaction.
  • La couche de contrôleur agit comme une colle entre les deux autres couches, les données en mouvement entre elles.

La représentation habituelle du modèle MVC ressemble à ceci:

Simplistic view of the MVC pattern

Le problème est que ce diagramme est erroné.

Ce "secret" se cache en pleine vue dans quelques lignes dans la documentation d'Apple:

"On peut fusionner les rôles MVC joués par un objet, ce qui fait qu'un objet remplit à la fois le contrôleur et les rôles de vue—auquel cas il s'appellerait un contrôleur de vue. De la même manière, vous pouvez également avoir des objets de contrôleur de modèle. "

De nombreux développeurs pensent que les contrôleurs de vue sont les seuls contrôleurs qui existent dans une application iOS. Pour cette raison, beaucoup de code finissent par être écrits en leur absence d'un meilleur endroit. C'est ce qui amène les développeurs à utiliser des singletons lorsqu'ils doivent propager l'état: cela semble être la seule solution possible.

D'après les lignes citées ci-dessus, il est clair que nous pouvons ajouter une nouvelle entité à notre compréhension du modèle MVC: le contrôleur de modèle. Les modèles de contrôleurs traitent du modèle de l'application, remplissant les rôles que le modèle lui-même ne devrait pas remplir. C'est en fait la façon dont le schéma ci-dessus devrait ressembler:

Diagram of the MVC pattern updated with view and model controllers

L'exemple parfait d'un contrôleur modèle est utile pour conserver l'état de l'application. Le modèle ne doit représenter que les données de votre application. L'état de l'application ne devrait pas être son préoccupation.

Ce maintien de l'état se termine habituellement dans les contrôleurs de vue, mais maintenant nous avons un nouvel et meilleur endroit pour le mettre: un contrôleur de modèle. Ce contrôleur de modèle peut ensuite être transmis aux contrôleurs de vue lorsqu'ils apparaissent sur l'écran grâce à une injection de dépendance.

Nous avons résolu le singleton anti-pattern. Voyons notre solution en pratique avec un exemple.

Propagation de l'état des contrôleurs de vue à l'aide de l'injection de dépendance

Nous allons écrire une application simple pour voir un exemple concret de la façon dont cela fonctionne. L'application affichera votre devis préféré sur un seul écran et vous permettra d'éditer le devis sur un deuxième écran.

Cela signifie que notre application aura besoin de deux contrôleurs de vue, qui devront partager l'état. Après avoir vu comment cette solution fonctionne, vous pouvez étendre le concept à des applications de toute taille et complexité.

Pour commencer, nous avons besoin d'un type de modèle pour représenter les données, ce qui, dans notre cas, est une citation. Cela peut se faire avec une structure simple:

Le modèle de contrôleur

Nous devons alors créer un contrôleur de modèle qui détient l'état de l'application. Ce contrôleur de modèle doit être une classe. C'est parce que nous aurons besoin d'une instance unique que nous passerons à tous nos contrôleurs de vues. Les types de valeur tels que les structures sont copiés lorsque nous les transmettons, de sorte qu'ils ne sont clairement pas la bonne solution.

Tous les besoins de notre modèle de contrôleur dans notre exemple est une propriété où il peut conserver le devis actuel. Mais, bien sûr, dans les applications plus grandes, les contrôleurs de modèles peuvent être plus complexes que ceci:

J'ai attribué une valeur par défaut à la propriété quote, de sorte que nous aurons déjà quelque chose à afficher à l'écran lorsque l'application sera lancée. Ceci n'est pas nécessaire et vous pouvez déclarer que la propriété est facultative initialisée à nil, si vous souhaitez que votre application démarre avec un état vide.

Créer l'interface utilisateur

Nous avons maintenant le contrôleur modèle, qui contiendra l'état de notre application. Ensuite, nous avons besoin des contrôleurs de vue qui représenteront les écrans de notre application.

Tout d'abord, nous créons leurs interfaces utilisateur. C'est ainsi que les deux contrôleurs de vue regardent dans le storyboard de l'application.

view controllers in the storyboard

L'interface du premier contrôleur de vue est composée de quelques étiquettes et d'un bouton, mises en place avec des contraintes de mise en page automatiques simples. (Vous pouvez en savoir plus sur la mise en page automatique ici sur Envato Tuts+.)

L'interface du second contrôleur de vue est la même, mais a une vue de texte pour éditer le texte de la citation et un champ de texte pour éditer l'auteur.

Les deux contrôleurs de vue sont connectés par une présentation modal simple suit, qui provient du bouton Edit quote.

Vous pouvez explorer l'interface et les contraintes des contrôleurs de vue dans le compte GitHub.

Coder un contrôleur de vue avec une injection de dépendance

Nous devons maintenant coder nos contrôleurs de vues. L'important, qu'il faut garder à l'esprit ici, c'est qu'ils doivent recevoir l'instance du contrôleur de modèle de l'extérieur, par l'intermédiaire de l'injection de dépendance. Ils doivent donc exposer une propriété à cette fin.

Nous pouvons appeler notre premier contrôleur de vue QuoteViewController. Ce contrôleur de vue a besoin de quelques sorties sur les étiquettes pour la citation et l'auteur dans son interface.

Lorsque ce contrôleur de visualisation vient à l'écran, nous remplissons son interface pour afficher le devis actuel. Nous mettons le code pour le faire dans la méthode viewWillAppear(_ :) du contrôleur.

Nous aurions pu mettre ce code dans la méthode viewDidLoad(), ce qui est assez courant. Le problème, cependant, est que viewDidLoad() est appelé une seule fois, lorsque le contrôleur de vue est créé. Dans notre application, nous devons mettre à jour l'interface utilisateur de QuoteViewController chaque fois qu'elle apparaît sur l'écran. C'est parce que l'utilisateur peut modifier le devis sur le deuxième écran.

C'est pourquoi nous utilisons la méthode viewWillAppear(_ :) au lieu de viewDidLoad(). De cette façon, nous pouvons mettre à jour l'UI du contrôleur de visualisation chaque fois qu'il apparaît sur l'écran. Si vous souhaitez en savoir plus sur le cycle de vie d'un contrôleur de visualisation et toutes les méthodes qui sont appelées, j'ai écrit un article détaillant tous ces éléments.

Le contrôleur Edit View

Nous devons maintenant coder le second contrôleur de vue. Nous appelerons ceci EditViewController.

Ce contrôleur de vue est comme le précédent:

  • Il a des sorties pour la vue de texte et le champ de texte que l'utilisateur utilisera pour modifier le devis.
  • Il possède une propriété pour l'injection de dépendance de l'instance du contrôleur de modèle.
  • Il remplit son interface utilisateur avant de venir à l'écran.

Dans ce cas, j'ai utilisé la méthode viewDidLoad() car ce contrôleur de vue ne s'affiche qu'une seule fois.

Partage de l'Etat

Nous devons maintenant passer l'état entre les deux contrôleurs de vue et le mettre à jour lorsque l'utilisateur modifie le devis.

Nous passons l'état de l'application dans la méthode prepare(for:sender:) du QuoteViewController. Cette méthode est déclenchée par la suite connectée lorsque l'utilisateur tape sur le bouton Edit quote.

Nous passons ici l'instance du ModelController qui conserve l'état de l'application. C'est là que l'injection de dépendance pour EditViewController se produit.

Dans EditViewController, nous devons mettre à jour l'état de la citation nouvellement saisie avant de revenir au contrôleur de vue précédent. Nous pouvons le faire dans une action connectée au bouton Save:

Initialiser le contrôleur de modèle

Nous sommes presque terminés, mais vous avez peut-être remarqué que nous manquons encore quelque chose: le QuoteViewController passe le ModelController au EditViewController via l'injection de dépendance. Mais qui donne cette instance à QuoteViewController en premier lieu? Rappelez-vous que lorsque vous utilisez une injection de dépendance, un contrôleur de vue ne doit pas créer ses propres dépendances. Ceux-ci doivent venir de l'extérieur.

Mais il n'y a pas de contrôleur de vue avant le QuoteViewController, car il s'agit du premier contrôleur de vue de notre application. Nous avons besoin d'un autre objet pour créer l'instance ModelController et la transmettre à QuoteViewController.

Cet objet est AppDelegate. Le rôle du délégué de l'application est de répondre aux méthodes de cycle de vie de l'application et de configurer l'application en conséquence. L'une de ces méthodes est l'application (_:didFinishLaunchingWithOptions:), qui est appelée dès l'ouverture de l'application. C'est là que nous créons l'instance du ModelController et le passons à QuoteViewController:

Notre application est maintenant terminée. Chaque contrôleur de vue a accès à l'état global de l'application, mais nous n'utilisons pas de singlet dans notre code.

Vous pouvez télécharger le projet Xcode pour cette application exemple dans le tutoriel GitHub repo.

Conclusions

Dans cet article, vous avez vu comment l'utilisation de singletons pour propager l'état dans une application iOS est une mauvaise pratique. Les singletons crée beaucoup de problèmes, bien qu'il soit très facile à créer et à utiliser.

Nous avons résolu le problème en examinant de plus près le modèle MVC et en comprenant les possibilités cachées. Grâce à l'utilisation de contrôleurs de modèle et d'injection de dépendance, nous avons pu propager l'état de l'application sur tous les contrôleurs de vue sans utiliser de singlet.

Il s'agit d'une application d'exemple simple, mais le concept peut être généralisé à des applications de toute complexité. C'est la meilleure pratique standard pour propager l'état dans les applications iOS. Je l'utilise maintenant dans chaque application que j'écris pour mes clients.

Quelques choses à garder à l'esprit lorsque vous élargissez le concept aux applications plus grandes:

  • Le contrôleur de modèle peut enregistrer l'état de l'application, par exemple dans un fichier. De cette façon, nos données seront rappelées chaque fois que nous fermerons l'application. Vous pouvez également utiliser une solution de stockage plus complexe, par exemple Core Data. Ma recommandation est de garder cette fonctionnalité dans un contrôleur de modèle distinct qui ne prend en charge que le stockage. Ce contrôleur peut ensuite être utilisé par le contrôleur modèle qui conserve l'état de l'application.
  • Dans une application avec un flux plus complexe, vous disposerez de nombreux conteneurs dans votre flux d'applications. Ceux-ci sont généralement des contrôleurs de navigation, avec le contrôleur occasionnel de la barre d'onglets. La notion d'injection de dépendance s'applique toujours, mais vous devez prendre en compte les conteneurs. Vous pouvez soit creuser dans leurs contrôleurs de vue contenus lors de l'injection de dépendance, soit créer des sous-classes de conteneur personnalisées qui passent le contrôleur de modèle.
  • Si vous ajoutez du réseautage à votre application, cela devrait également se faire dans un contrôleur de modèle distinct. Un contrôleur de vue peut effectuer une demande de réseau via ce contrôleur de réseau, puis transmettre les données résultantes au contrôleur de modèle qui conserve l'état. Rappelez-vous que le rôle d'un contrôleur de vue est exactement ceci: agir comme un objet de colle qui transmet les données entre des objets.

Restez à l'écoute pour plus de conseils et de meilleures pratiques de développement d'applications iOS!

Advertisement
Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.