Accueil Nos publications Blog Développer en ios : Allégez vos UIViewControllers (SoC : Separation of concerns)

Développer en ios : Allégez vos UIViewControllers (SoC : Separation of concerns)

multitasking_octopusGrâce à leur pouvoir d’arranger et de séparer les rôles, les Design Patterns sont un sujet qui ne cesse de déclencher des débats au sein des communautés de développeurs. Chacun essaye d’adapter ces outils à la technologie qu’il utilise, et avec les moyens de bord qui lui sont proposés. Dans cet article nous allons nous intéresser au plus connu des patrons, MVC (Modèle-Vue-Contrôleur), dont nous allons décortiquer plus particulièrement la version Cocoa.

 

L’architecture MVC en théorie

Les Design Patterns étant présents dans la plupart des cursus informatique, vous en avez sûrement entendu parler. Définir le MVC n’est pas mon premier souci, par contre l’appliquer et respecter ses exigences dans une application iOS l’est. Un peu de théorie avant de plonger dans le vif du sujet nous sera sans doute d’une grande utilité pour la suite.

Brièvement, MVC (pour Modèle-Vue-Contrôleur) est une sorte de paradigme qui, au sein d’une application, vise à écarter les données et leur vie (Modèle) de leur présentation (Vue) par l’intermédiaire d’un gestionnaire d’événements (Contrôleur). Je vous laisse le soin de plonger dans l’histoire si vous le désirez, puisqu’il est toujours intéressant de connaître la provenance d’une théorie, du moment que ça nous aide à cerner les problématiques qui en sont à l’origine.

Ce qu’il faut surtout retenir, c’est que MVC n’est pas spécifiquement destiné à une technologie ou un langage, c’est tout simplement une discipline de conception qui :

  • Sera d’une grande utilité pour se rappeler de qui fait quoi dans son application.
  • Apporte une clarté dans l’architecture qu’il impose, en découplant le fond de la forme et en séparant les responsabilités. On gagne, en l’occurrence, en temps de maintenance et d’évolution.
  • Apporte une plus grande souplesse pour organiser le développement entre différents développeurs.

Par ailleurs, MVC s’avère disproportionné pour des applications de faible envergure qui n’évoluent jamais. L’application de MVC impliquant plus de fichiers et plus de code, on ne veut pas se compliquer la vie non plus pour une application qui parse un flux RSS et l’affiche.

 

Vision Cocoa du MVC

Controller Objects Tie the Model to the View: sketches the abstract outline of a controller object, but in practice the picture is far more complex” (documentation Apple)

Apple encourage, dans sa documentation, à utiliser au mieux le modèle MVC, dont la philosophie Cocoa est la suivante :

  • Faire de telle sorte que chaque classe ajoutée soit dans l’un des groupes de classes du modèle MVC, à savoir, modèles, vues ou contrôleurs.
  • On peut à tout moment réutiliser une classe modèle ou une vue. Les contrôleurs font le job d’intermédiaire et ne sont donc pas censés être réutilisés.
  • On peut avoir des rôles combinés dans une même classe (Vue contrôleur ou modèle contrôleur). En revanche, il faut toujours garder un bon niveau d’abstraction permettant de bien définir les rôles de chacun.
  • Limiter au maximum les dépendances entre les classes utilisées.

En fait, Cocoa adopte profondément le modèle MVC dans ses architectures internes et ses technologies et ceci est très clair dans, par exemple, document architecture et bindings. Néanmoins, Apple communique peu sur ce qu’une bonne application mobile devrait suivre comme architecture pour être dans les normes. Mais a-t-on vraiment besoin d’Apple pour nous dicter ce qu’il faut faire en matière d’organisation?

 

Et en clair?

Étant développeur moi-même, je sais que notre priorité est de sortir une appli attirante et qui répond au besoin. On a tendance à négliger la partie qui traite la pérennité du code, d’autant plus que faire le ménage dans son code implique une certaine gymnastique mentale. Apple ne nous facilite pas la tâche non plus, contrairement à Microsoft, qui a bien mis en exergue l’architecture MVC dans son framework ASP .NET MVC. Ainsi, l’organisation de l’architecture saute aux yeux quand vous créez un nouveau projet sous Visual Studio en choisissant le Template ASP .NET MVC :

ASPMVC

 

A l’inverse, quand vous créez un nouveau projet dans Xcode, il vous génère une classe Delegate, un UIViewController et un Storyboard.

screen-xcode

 

De même, les ingénieurs Apple ne nous montrent pas grand chose dans les WWDC, et se contentent de faire marcher leurs programmes, même si cela implique la négligence des aspects MVC, en créant, par exemple, des “custom constraints” dans des viewDidLoad de UIViewControllers.

DearApple

 

Attendez… Vous avez dit UIViewController Mais comment ça se fait ? UIView + Controller dans le même composant ? Euh là j’avoue je suis un peu confus !”  

Vous avez parfaitement raison, permettez-moi de vous l’annoncer, un UIViewController n’est autre qu’un contrôleur. En fait, Apple a fait de son mieux pour faciliter la communication entre les différentes couches et, sur ce point-là, a privilégié la vue.

Nous avons pris l’habitude d’utiliser les UIViewControllers comme le centre de tout le développement. C’est normal, puisque le code que vous trouvez sur Internet ou qu’Apple fournit est souvent construit ainsi, car il ne met en avant qu’une ou deux fonctionnalités, sans avoir besoin de se soucier d’adopter une architecture sophistiquée. Par contre, lorsque notre appli commence à prendre de l’envergure, et surtout lorsque nous nous apercevons qu’on peut réutiliser des fonctionnalités développées dans des applis précédentes, on avise le besoin d’apporter beaucoup de ménage à notre code.

 

UIViewController le centre de l’univers iOS

Je vais commencer cette partie par un tweet qui a fait grand bruit dans les communautés de développeurs de la pomme. Le monsieur s’appelle Colin Campbell, un développeur iOS :

tweetVC

 

Ce n’est pas faux, Apple a fait du UIViewController son enfant préféré, qui a accès à tout. C’est vraisemblablement le squelette d’une application iOS. Vous allez me dire : “ce n’est pas censé être le cas ? C’est un contrôleur, c’est lui qui va coordonner les tâches.” Je ne peux pas dire le contraire, mais c’est abusé quand même : à force de rendre les UIViewControllers tellement accessibles et polyvalents, on tombe souvent dans le piège et on entasse toute sorte de fonctionnalités dedans.

Par exemple, il est très facile d’entasser du code dans un IBAction (appel d’événement), ou un appel de Delegate, et de faire tout au même endroit. On assiste parfois, même dans le code fourni par Apple, à des créations de contrôles (des héritages de la classe UIView : UIButton, UILabel…) dans des méthodes qui appartiennent aux UIViewControllers. Je vous rappelle que MVC impose la création et la personnalisation des contrôles dans la couche vue. Je ne saurais trop insister sur l’importance de cerner le champ d’action des IBOutlets, qui sont là pour faire passer des messages entre la vue et le contrôleur, et non pour personnaliser l’apparence.

Un autre exemple, qui passe inaperçu, est l’utilisation des Callbacks et des appels de Delegate dans les UIViewControllers. Imaginez que l’un de vos UIViewControllers utilise des fonctionnalités qui sont en rapport avec les classes MFMailComposeViewController, UIScrollView et CLLocationManager. Imaginez maintenant que vous allez implémenter leurs appels de méthodes delegates dans le même UIViewController (et c’est ce qui se passe habituellement). Vous allez vite vous trouver dans un mélange hétéroclite de fonctionnalités, avec des centaines, voire des milliers, de lignes de code et vous allez à peine réussir à vous situer dans votre propre code!

Regardez, par exemple, lorsque vous créez un UITableViewController. A partir du moment où vous allez commencer à customiser votre TableView dans l’implémentation du UIViewController (taille des cellules, couleur de labels…), vous tombez dans le piège et vous sortez du cadre MVC.

thisisMVC

 

Utilisez plutôt des héritages de la classe UITableViewCell pour personnaliser les cellules de votre tableau. De cette façon, vous séparerez les rôles, vous aurez un code beaucoup plus compréhensible (au lieu d’agglomérer tout dans le UIViewController) et vous pourrez le réutiliser dans d’autres projets.

Pour conclure, Apple encourage fortement l’utilisation du modèle MVC, l’utilisant elle-même dans beaucoup de ses technos. En revanche, il faut bien comprendre la philosophie Cocoa dans la conception des applications mobiles. Le premier but d’Apple est de faciliter la vie aux développeurs, pas de les induire en erreur. Maintenant, et avec l’avènement des applications mobiles volumineuses, je pense qu’un bon design s’impose. Et voici pour finir, quelques conseils pour avoir un code plus propre et plus conforme au design MVC.

 

Bonnes pratiques

Même si Apple autorise les “MVC combining roles”, c’est à dire la possibilité de coupler contrôleur-vue ou contrôleur-modèle, elle conseille de limiter leur utilisation. En effet, la séparation des rôles est de loin la meilleure stratégie, du fait de sa réutilisabilité des objets et l’extensibilité de l’app.

Le but principal d’une application bien conçue selon le modèle MVC, du moins théoriquement, devrait être de pouvoir réutiliser autant d’objets que possible. Dans notre contexte, il s’avère que garder un bon niveau de réutilisabilité des UIViewControllers est un peu délicat, ces derniers jouant toujours le rôle d’intermédiaires, sans oublier qu’ils s’occupent généralement des comportements spécifiques des applications tels que les navigations et l’échange des données transversales. Par contre, pensez toujours à garder un très bon niveau d’abstraction pour les classes modèles et vues.

  • Une classe vue ne doit pas dépendre d’une classe modèle.
  • Une classe vue ne doit pas dépendre d’un contrôleur.
  • Une classe modèle ne doit dépendre que d’autres classes modèles.
  • Pensez à bien structurer votre code et catégoriser vos classes dans des dossiers. Certains utilisent la division des catégories de classe en trois dossiers à savoir : Contrôleurs, Modèles et vues. D’autres scindent leurs classes par écran, chaque dossier se nommera du nom de l’écran en question et va contenir la totalité des classes en rapport avec. Personnellement, j’opte pour la combinaison des deux : j’ai trois dossiers MVC et, sous chaque dossier, j’ai des dossiers par écran, qui m’aideront à me situer facilement. Pensez également à consacrer un dossier “Libs” pour les tierces parties, un dossier “Graphics” pour le graphique de l’appli, et un dossier “utils” ou “helpers” pour les classes transverses (généralement de type singleton) qui vous aideront dans des fonctionnalités répétitives.
  • Délocalisez autant que possible votre logique métier dans des catégories de classes modèles. Par exemple, si vous êtes amenés à calculer la somme des salaires d’une entreprise, faites-le dans une méthode qui retournera un Double, dans une catégorie de la classe Enterprise (qui aura comme nom par exemple Enterprise+Logic).
  • Faites vos appels de webservice dans les classes Store, qui résideront dans une couche service, là où vous mettez le téléchargement des données et les techniques de cache. Si vous avez besoin d’une information qui sera utilisée directement dans le UIViewController, comme la gestion des erreurs de connexion par exemple, faites appel aux mécanismes de callback comme les “completion block”.
  • Évitez d’écrire des initialisations de composants graphiques ou des spécifications en rapport avec des vues dans un UIViewController. Utilisez plutôt les fichiers .XIB ou les storyboards. Si vous avez des hiérarchies compliquées, faites appels à des classes d’héritage de UIView.
  • Il existe plusieurs techniques de communication entre les différents objets, entre autres, le target-action pour la communication vue-contrôleur et les Delegates pour la communication modèle-contrôleur. Par contre, les techniques de communication entre contrôleurs sont assez rares et nous font souvent tomber dans l’erreur du bad design. Vous ferez bien de déléguer cette tâche à un autre objet séparé, qui sera en mesure d’effectuer des communications entre UIViewControllers. Faites bien attention à la gestion mémoire, utilisez des “weak references” pour éviter les “retain cycle”.
  • Vous pouvez également utiliser une technique qu’Apple a ouvert aux développeurs depuis iOS5 (avant elle détenait l’exclusivité de son utilisation): le “View Controller Containment”. Cette technique propose d’inclure des UIViewControllers fils dans des UIViewControllers parents, et de diviser les tâches entre plusieurs UIViewControllers, ce qui nous permet d’avoir des UIViewControllers hautement qualifiés pour être réutilisés.

 

Vous l’aurez compris, MVC est indépendant du langage de programmation, puisque son intérêt concret se situe dans l’isolation des opérations, laissant le soin à chacun de ne s’occuper que de sa partie. Du fait des contraintes qu’impose le développement mobile, la philosophie d’Apple quant à cette technologie se voit nuancée. Essayez tout de même d’organiser votre code en fonction de vos besoins, conservez ce modèle en background, maintenez une vision à long terme, et vous allez voir que MVC vous fera gagner beaucoup de temps !