Avec ou sans storyboard ?

iOS 1 sept. 2016

C'est la question qui déchire parmi les développeurs iOS : doit-on utiliser les Storyboards ou doit-on tout faire avec nos jolies petites mimines sur le clavier ?

Et bien je dois avouer faire partie de ceux qui aiment les storyboards...jusqu'à un certain point. Et oui, malheureusement ce n'est pas aussi simple qu'une réponse par oui ou non, voici quelques éléments de réponse.

Ce que j'aime dans les Storyboards

Les Storyboards me permettent de créer rapidement des interfaces graphiques et d'avoir une vision globale de l'expérience utilisateur. Et puis il y a les <3 outlets <3 ...

Ils sont aussi très agréables pour utiliser AutoLayout, surtout quand on compare à l'API permettant de le faire en Swift/Objective C. Si en plus on doit gérer des variantes en fonction des tailles d'écran je pense qu'ils deviennent indispensables.

Pour cette raison je me sers principalement des Storyboards pour créer mon interface initiale et avoir un aperçu rapide du résultat final.

Mais...

Ce qui me gêne dans les Storyboards

Même si je les aime bien, ils y a plusieurs choses qui me posent problème dans l'utilisation de Interface Builder. Je ne parlerai pas du côté statique des Storyboard car c'est plus une limitation incontournable plutôt qu'un véritable défaut à mon avis.

Tout d'abord ce sont les lenteurs auxquelles on a droit à l'utilisation de certains Storyboards dans XCode ; je ne parle pas de performances sur l'app finale mais bien de l'utilisation de Interface Builder par nous les développeurs. Parfois c'est à s'arracher les cheveux, alors même que j'utilise un MacBook Pro Retina avec SSD et pas mal de RAM qui n'est jamais essoufflé...sauf pour Interface Builder. Et puis ce n'est pas systématique, certain Storyboards s'utilisent très bien, d'autres sont très lent. La meilleure solution (mais pas parfaite) que j'ai pu trouver pour le moment est de découper les Storyboards en plusieurs en utilisant les références. Je n'ai pas encore essayé de réouvrir des gros storyboards dans XCode 8, peut-être que les lenteurs sont une histoire du passé.

L'autre soucis c'est qu'on a parfois du mal à se rappeler des propriétés que l'on a modifié car elles sont un peu éparpillées dans les différents inspecteurs. On se retrouve souvent avec une partie des réglages dans les Storyboards et une autre partie dans le code en Swift ; parfois on ne sait plus trop pourquoi l'objet a cette apparence et surtout à quel endroit on y a contribué.

Le dernier problème est significatif mais il ne concerne que ceux qui travaillent en équipe : les modifications concurrentes. Tous les développeurs utilisent des gestionnaires de version (Git, Subversion, etc.) qui fonctionnent à merveille pour éditer des fichier texte à plusieurs et garder l'historique de tous les changements. Les storyboards sont bien des fichiers texte (XML), mais chaque édition de l'interface gérnère d'énormes modifications dans le fichier. Si vous travaillez à plusieurs sur un même fichier, alors il devient quasiment impossible de fusionner les modifications de chacun sans conflict. La solution consiste souvent à utiliser un storyboard par écran ou tout simplement à revenir aux ancêtre des storyboard : les fichiers xib. Les fichiers .xib proposent quasiment les mêmes fonctionnalités que les storyboards pour éditer un écran, mais ne permettent pas de gérer les transitions entre les écrans. Vous en aurez donc un par contrôleur de vue.

Une solution intermédiaire

Il y a quelques jours je suis tombé sur une conférence super intéressante donnée par Nick O'Neill sur des modèles de code en Swift. Et bien son idée m'a paru être la meilleure façon de mixer les 2 mondes en utilisant la fonctionnalité didSet des outlets.

Voici ce qu'il propose :


@IBOutlet weak var arrivalLabel: UILabel! {
        didSet {
            arrivalLabel.text = "Arriving in 10 minutes".uppercaseString
            arrivalLabel.font = UIFont(name: "Lato", size: 11)
            arrivalLabel.textColor = UIColor.blueColor()
            arrivalLabel.textAlignment = .Center
            arrivalLabel.numberOfLines = 1
        }
    }
  

Pour rappel, didSet exécute le code entre accolades juste après avoir reçu une valeur pour la propriété arrivalLabel. Or, il s'avère que cette valeur est affectée au moment du chargement du Storyboard puisqu'on utilise ici un IBOutlet. Finalement ce code s'exécute peu ou prou au même moment que la méthode viewDidLoad de notre UIViewController.

On pourrait faire toutes les configurations dans viewDidLoad, c'est d'ailleurs ce que font beaucoup de développeurs iOS (moi y compris), mais si vous avez plusieurs vues votre fonction va vite devenir immense et pas forcément très jolie. Ici vous regroupez tous les réglages de vos vues au même endroit, tout en découpant efficacement par vue ; c'est clair, net et précis : j'adore !

Attention cependant, ne faites pas référence aux autres vues/outlets dans la méthode didSet d'un outlet car la seule garantie que vous avez est que cette vue a été connectée, pas les autres. Si vous avez besoin de configurer une vue en vous basant sur une autre, cela doit être fait dans le viewDidLoad qui vous garanti que tous les outlets ont bien été connectés.

Voici donc le compromis que je propose pour utiliser les Storyboards en restant efficace :

  • Garder des Storyboards légers, ne dépassant pas 3 ou 4 écrans.
  • Faire la mise en page en utilisant AutoLayout dans Interface Builder (Storyboard)
  • Faire les réglages de vues en Swift en utilisant le didSet sur les outlets
  • (optionel) Faire aussi les réglages de vues dans le Storyboard si vous voulez que l'aperçu corresponde à la réalité.

Si vous avez d'autres idées ou remarques sur l'utilisation des Storyboards, n'hésitez pas à poster un commentaire ci-dessous pour en discuter.

Happy coding!

Max

Mots clés