HandsAppMVP: architecture iOS pour l'externalisation du développement de studio

image

Un bon code commence par l'architecture et les applications iOS ne font pas exception. Il existe de nombreux modèles standard, mais le but de cet article n'est pas d'en parler, mais de l'expérience d'adapter l'un d'eux et de développer le vôtre. Nous avons appelé cette adaptation HandsAppMVP.

image

Dans le développement iOS, l'architecture détermine principalement l'organisation des classes et des dépendances pour un ViewController spécifique. Cependant, le composant central peut être non seulement lui, mais simplement UIView. Le choix dépend de la tâche spécifique.

Comparaison d'architecture


Il existe plusieurs modèles d'architecture standard pour iOS: MVC, MVP, MVVM, VIPER (des liens vers la description de chacun se trouvent à la fin de l'article).

En choisissant une architecture de développement, nous avons identifié les principaux paramètres auxquels elle devrait correspondre: la vitesse de développement, la flexibilité et un seuil d'entrée bas. Ensuite, nous avons commencé à comparer trois architectures bien connues avec ces paramètres à l'esprit (le modèle de communauté MVC iOS a longtemps été enterré en raison d'une non-conformité flagrante avec une seule responsabilité).

Pour une équipe d'externalisation, la vitesse de développement est particulièrement importante. VIPER est l'architecture la plus complexe et la plus «lente», le développement est plus rapide en utilisant du MVP pur ou du MVVM, car ils ont moins de composants.

La flexibilité signifie l'ajout ou la suppression indolore de fonctionnalités dans l'application. Ce paramètre est fortement corrélé à la vitesse de développement à toutes les étapes de la vie de l'application, à l'exception de la phase initiale. La flexibilité est également étroitement liée à la simplicité des tests - les tests automatiques donnent au développeur la certitude qu'il ne cassera rien et aidera à éviter les bugs. MVP classique est mal couvert par les tests, surtout si vous n'utilisez pas les interfaces de classe décrites ci-dessous. MVVM du point de vue des tests a également de mauvaises performances, car le test du code réactif prend beaucoup plus de temps. VIPER est idéal pour écrire des tests car il respecte autant que possible le principe de l'unique responsabilité et les cours dépendent des abstractions.

Et le dernier paramètre que nous avons considéré est le seuil d'entrée. Il montre à quelle vitesse les nouveaux développeurs (tout d'abord, les jones) pénètrent dans l'architecture. Ici, MVVM utilisant des bibliothèques réactives tierces (RxSwift, PromiseKit, etc.) occupe une dernière place honorable pour des raisons évidentes. VIPER est également une architecture assez complexe en raison du grand nombre de composants. MVP a le seuil d'entrée le plus bas.

Après avoir pesé le pour et le contre, nous sommes arrivés à la conclusion que nous avons besoin de quelque chose d'aussi simple que MVP, et aussi flexible que VIPER. L'idée est donc née de créer leur propre architecture basée sur eux - HandsAppMVP.

Développez MVP


Les principaux composants de notre architecture sont Model, View, Presenter. Ils remplissent les mêmes fonctions que dans le MVP classique selon le schéma bien connu:

image
[Schéma du MVP classique]

Ici et ci-dessous, chaque composant d'interaction (carré bleu) dans les diagrammes est une classe dont la durée de vie coïncide avec la durée de vie de la vue. Une flèche pleine indique la propriété d'un autre objet, un lien fort et une flèche pointillée indique un lien faible. Avec des références faibles, nous évitons les dépendances circulaires et les fuites de mémoire.

Interfaces


Tout d'abord, nous avons ajouté les interfaces ViewInput et ViewOutput à ce schéma classique. Nous avons pris en compte le cinquième principe de SOLID - le principe de l'inversion de dépendance. Ce n'est probablement pas un ajout, mais un raffinement pour MVP. La dépendance aux abstractions permet de se débarrasser de la connectivité stricte des composants et vous permet d'écrire des tests normalement. Le schéma concernant les interfaces ressemble à ceci:

image
[Ajouter des interfaces ViewInput et ViewOutput] Un

petit rectangle est une interface.

Un développeur attentif demandera, où sont les interfaces pour Model? Maintenant, nous nous tournons vers eux.

Travailler avec des données


Le modèle de données dans les architectures mobiles est un concept collectif. Un exemple standard: une application frappe sur le réseau pour interagir avec le serveur, puis enregistre les données dans CoreData pour le travail hors ligne, écrit quelques informations simples dans UserDefaults et stocke le JWT dans le trousseau. Toutes ces données qui interagissent constituent le modèle.

La classe qui est chargée d'interagir avec le conteneur de données d'un type particulier, nous appelons le service de données. Pour chaque conteneur (base de données distante, base de données locale, UserDefaults, etc.), une classe de service est ajoutée à HandsAppMVP qui interagit avec le présentateur. Maintenant, vous pouvez également ajouter des interfaces d'entrée / sortie pour chaque service de données:

image
[Ajout de services pour travailler avec des données]

Toutes les classes de service n'ont pas besoin d'être connectées au présentateur à l'aide d'une interface, comme, par exemple, lors de l'utilisation de Moya. Moya est une bibliothèque réseau open source. Moya fournit une classe de service prête à l'emploi (MoyaProvider), et lors de l'écriture de tests, nous n'avons pas à créer un objet factice qui remplace ApiProvider. Moya fournit un mode de test spécial, lorsqu'il est activé, MoyaProvider ne frappe pas sur le réseau, mais renvoie des données de test (plus de détails peuvent être trouvés ici). Dans ce cas, le présentateur ne fait pas référence à l'abstraction MoyaProvider, mais à l'implémentation. Et nous recevons des commentaires de ce service à l'aide de fermetures. Un exemple d'implémentation peut être trouvé dans le projet de démonstration.

Cet exemple est plus une exception qu'une règle et montre que l'adhésion à SOLID n'est pas toujours la meilleure solution.

La navigation


Nous considérons la navigation dans l'application comme une responsabilité distincte. HandsAppMVP utilise une classe spéciale pour cela - le routeur. Le routeur contient un lien faible vers la vue, avec lequel il peut afficher un nouvel écran ou fermer l'actuel. Le routeur interagit également avec le présentateur à l'aide de l'interface RouterInput:

image
[Ajout d'un composant pour la navigation (routeur)]

Assemblage de composants


Le dernier ajout au MVP classique que nous utilisons est Assembly, une classe collector. Il est utilisé pour initialiser la vue et d'autres composants de HandsAppMVP, ainsi que pour implémenter des dépendances. L'assembly contient la seule méthode publique - `assembler () -> UIViewController`, dont le résultat est le UIViewController (ou UIView) souhaité avec le graphe de dépendance nécessaire.

Nous ne montrerons pas Assembly dans le diagramme d'architecture, car il n'est pas connecté aux composants MVP et son cycle de vie se termine immédiatement après leur création.

Génération de code


Pour gagner du temps, nous avons automatisé le processus de création de classes HandsAppMVP à l'aide de Generamba. Les modèles utilisés pour Generamba se trouvent dans notre référentiel. Un exemple de configuration pour Generamba se trouve dans le projet de démonstration.

À la suite de la génération d'un écran spécifique, nous obtenons un ensemble de classes correspondant au schéma HandsAppMVP, un ensemble de tests unitaires pour créer et implémenter des composants, ainsi qu'une classe de modèle pour les tests de présentateur.

Qu'est-il arrivé


Si vous comparez Head-to-Head HandsAppMVP et VIPER, vous remarquerez qu'ils sont très similaires et le premier ne se distingue que par l'absence du composant Interactor. Mais, en se débarrassant de la couche entre les services et le présent (l'interacteur), ainsi qu'en simplifiant l'interaction avec le réseau en utilisant Moya, nous avons reçu une augmentation notable de la vitesse de développement.

Nous vous conseillons de prêter suffisamment d'attention à l'architecture au stade de la conception afin d'éviter les erreurs globales, les litiges avec les clients et les tourments des développeurs à l'avenir, et de diriger le processus de développement de manière compétente et prévisible.

N'oubliez pas que toute architecture peut ne pas convenir spécifiquement à votre projet, alors ne vous précipitez pas pour vous accrocher aveuglément à des modèles prêts à l'emploi et à des histoires réussies de leur application. N'ayez pas peur de développer et d'appliquer vos solutions - elles peuvent devenir plus précieuses et flexibles pour vous que les solutions toutes faites.

En conclusion, nous vous recommanderons de bons articles sur l'architecture des applications iOS qui nous ont aidés à comprendre les subtilités et à faire un choix:

  1. Modèles architecturaux dans iOS
  2. iOS Swift: architecture MVP
  3. Analyse de l'architecture VIPER sur l'exemple d'une petite application iOS sur Swift 4
  4. Implémentez MVVM sur iOS avec RxSwift

La documentation open source de SurfStudio a également été très utile et inspirée .

Enfin, nous mettons un lien sur le projet DEMO , écrit en HandsAppMVP, que nous avons mentionné à plusieurs reprises dans l'article.

All Articles