Code

Est-ce le bon moment de passer à SwiftUI ?

Retour au blog

Depuis la sortie d'iOS en 2007, les applications n’ont eu que UIKit pour créer et gérer les interfaces. Malgré ses améliorations régulières, avec notamment la possibilité d’utiliser différents moteurs de mise en page (positionnement manuel des vues, puis relatives grâce à AutoLayout), il pèche aujourd’hui par son manque de modernité et peine à séduire.


Présenté comme une nouvelle révolution depuis son annonce à la WWDC 2019, SwiftUI, de par sa syntaxe déclarative, est censé réduire drastiquement l'effort fourni pour construire une interface "simple". Cependant, sortir du cadre de cette simplicité prévue par Apple, c’est s’exposer à un accroissement considérable de la complexité du développement.

Faisons un tour d’horizon de SwiftUI et découvrez notre retour d’expérience.

Le fonctionnement de SwiftUI

Une application présente de manière logique une hiérarchie de vues. Voici un exemple de création de cette hiérarchie, où l'on peut voir l'intérêt d'un framework de declarative UI :

let imageView = UIImageView(
    image: UIImage(
        systemName: "flame.circle",
        withConfiguration: UIImage.SymbolConfiguration(pointSize: 60, weight: .light)
    )
)
imageView.contentMode = .scaleAspectFit
imageView.tintColor = .red
let label = UILabel()
label.text = "Hello, world"
label.textColor = .white
label.translatesAutoresizingMaskIntoConstraints = false
let labelContainerView = UIView()
labelContainerView.backgroundColor = .red
labelContainerView.addSubview(label)
labelContainerView.addConstraints([
    labelContainerView.topAnchor.constraint(equalTo: label.topAnchor, constant: -16),
    labelContainerView.bottomAnchor.constraint(equalTo: label.bottomAnchor, constant: 16),
    labelContainerView.leadingAnchor.constraint(equalTo: label.leadingAnchor, constant: -16),
    labelContainerView.trailingAnchor.constraint(equalTo: label.trailingAnchor, constant: 16),
])
let stackView = UIStackView(arrangedSubviews: [imageView, labelContainerView])
stackView.axis = .vertical
stackView.spacing = 20
stackView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(stackView)
view.addConstraints([
    stackView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
    stackView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
])
VStack(spacing: 20) {
    Image(systemName: "flame.circle")
        .font(.system(size: 60, weight: .light))
        .foregroundColor(.red)
    Text("Hello, world!")
        .foregroundColor(.white)
        .padding()
        .background(Color.red)
}

Ce changement de paradigme induit aussi une nouvelle façon de concevoir son programme. S'inspirant de frameworks de Declarative UI comme React, SwiftUI est pensé pour la programmation réactive, de plus en plus populaire, ce pour quoi UIKit n'était pas conçu à la base.

Frame 2 (1).jpg

L'application réagit à un événement (action de l'utilisateur, retour de webservice, timer, etc.) pour modifier son état en conséquence. À chaque modification du state, SwiftUI crée un nouvel arbre des vues et le compare à sa version précédente. Ainsi, seules les vues modifiées sont regénérées.

La création de l'arbre et la vérification des différences est peu coûteuse, utilisant la puissance des struct (value type) de Swift. Son fonctionnement est très bien expliqué par Apple dans cette vidéo : Demystify SwiftUI

Les avantages de SwiftUI

API haut niveau

L'API de SwiftUI est très simple, de par sa nature déclarative (Text, Image, VStack, ...).

L'abstraction est suffisante pour qu'Apple nous offre la compatibilité avec toutes ses plateformes. Une interface pour toutes, là où il aurait fallu adapter l'UI entre UIKit et AppKit par exemple.

Sa simplicité et sa proximité avec d'autres frameworks (React, Flutter, Jetpack Compose) sont une porte ouverte aux développeurs des autres plateformes à venir s'essayer au développement natif sur les plateformes Apple.

Interopérabilité

L'interopérabilité est une des forces majeures de SwiftUI. Son utilisation peut-être progressive, car il s'interface parfaitement avec UIKit et AppKit :

  • il est possible d'ajouter du code SwiftUI dans une application existante (via UIHostingController),
  • ou d'ajouter du code UIKit dans une nouvelle application avec interface déclarative (via UIViewRepresentable), pour compenser les limites actuelles de ce jeune framework.

Canvas interactif

Son intégration profonde avec Xcode en fait un atout considérable. Grâce aux Previews, il est possible de visualiser directement le rendu d'une vue SwiftUI. Tout changement dans le code recharge aussitôt la vue, avec laquelle on peut interagir. Mieux encore, il est possible d’avoir un rendu simultané sur différentes configurations d’écran.

xcode_preview.png

À l'instar des Storyboards, Xcode Previews est un éditeur dans lequel on peut venir glisser-déposer des éléments d'interface (Text, Button, ...). La différence - majeure - est que le code Swift est directement modifié ; terminé les fichiers .xml illisibles.

Les inconvénients de SwiftUI

Un framework mobile qui est encore très jeune

Étant un framework très récent, SwiftUI a encore une grosse marge de progression. Lors de la sortie de la version 1.0, d'aucuns pensaient qu'il était peut-être sorti trop tôt. Apple devait vouloir se confronter à sa communauté de développeurs pour pouvoir le faire évoluer au mieux en fonction des retours. Considéré comme "production ready" depuis la 2.0, il pêche encore par son manque de possibilités comparé à UIKit/AppKit.

La communauté - principalement celle d'iOS - est conséquente, mais le contenu proposé sur internet étant relativement récent, il se peut que les questions que l'on se pose soient actuellement sans réponse.

Son infrastructure reposant sur UIKit & AppKit

Sous les belles API de SwiftUI se cachent des composants implémentés avec UIKit/AppKit. Si on peut imaginer que le futur est radieux pour SwiftUI et qu'il finisse par se détacher complètement de ses prédécesseurs, aujourd'hui il repose entièrement dessus.

Il profite bien sûr de leurs solides fondations, ce qui permet son interopérabilité et son adaptation rapide. Cependant cela définit un cadre trop restreint par rapport aux immenses possibilités des frameworks sur lesquels il a été bâti.

Sortir des cas "simples" prévus par Apple nous amène toujours à nous frotter aux limites du framework. Toutes les API UIKit/AppKit n'ont pas encore été portées en SwiftUI, et construire une application complexe sans utiliser UIKit directement n'est actuellement pas envisageable.

Notre expérience avec le framework SwiftUI

Quelque peu frileux au début, nous avons attendu un certain temps avant de nous lancer :

  • comme lors du lancement de Swift, la version 1.0 était encore trop jeune pour être considérée réellement utilisable.
  • nous ne souhaitions pas restreindre les applications à la dernière version d'iOS disponible. Travailler aujourd'hui avec SwiftUI 2.0 permet d'avoir une deployment target minimum à iOS 14.

Les plaisirs que nous apprécions

Les avantages de SwiftUI se ressentent instantanément.

Créer une nouvelle vue de manière déclarative est une expérience bien plus plaisante que de définir des séries de contraintes pour placer nos éléments.

Utilisateurs de la programmation réactive depuis un petit temps déjà, nous reposer sur un framework UI conçu pour ça est intuitif et agréable.

Visualiser instantanément le résultat des modifications d'interface nous fait gagner un temps précieux et nous rappelle un des avantages de la programmation Web.

L'interopérabilité nous permet de pouvoir choisir, en fonction de nos besoins, avec quel framework créer notre interface.

Les frustrations que nous avons rencontrées

Tous pointeront du doigt le même problème : les limites du framework.

SwiftUI, même s'il est utilisable, n'est pas encore abouti. Combien de fois nous nous sommes retrouvés à devoir passer par UIKit après avoir compris que SwiftUI ne nous permettrait pas d'arriver au bout de la conception de notre interface ? Cette marche arrière est chronophage et particulièrement frustrante, car nous avions attendu une maturation du projet avant de l'utiliser.

De nombreuses choses faciles à mettre en place avec UIKit sont plus fastidieuses avec SwiftUI. En voici quelques exemples :

  • Pour accéder au contentOffset d'une UIScrollView paginée, on doit utiliser la désormais classique solution Color.clear - GeometryReader - PreferenceKey. Contraire au principe de "Less code".
  • Afficher un carrousel, avec un aperçu du bout de l'élément suivant, est aisé à faire via une UICollectionView. Pas de possibilité de "peek" disponible avec la TabView de SwiftUI. La communauté a heureusement apporté une solution avec SwiftUI-Pager.
  • La gestion de la navigation s’est refermée. Adieu les modifications de la navigation stack à la volée et l’affichage de modales comme bon nous semble. Il faut désormais respecter la logique de navigation à la lettre, s’armer de patience et penser au contexte lors du développement (ce qui peut impliquer de la duplication de code si on veut gérer l’affichage d’alertes globales peu importe la modale affichée, sous peine d’erreurs comme “Attempt to present X on Y which is already presenting Z”)
  • La status bar a longtemps été source de problèmes. Ils ont été grandement atténués notamment grâce à l'utilisation de preferredStatusBarStyle avec UIKit. Cependant, avec SwiftUI, il est devenu plus compliqué de gérer son changement de couleur. On fait un pas en arrière pour contourner les limitations, car on doit utiliser une API dépréciée.

Globalement, de nombreuses limitations d'API peuvent être compensées par l'utilisation de la librairie open source SwiftUI-Introspect qui permet d'accéder aux composants UIKit sous-jacents.

Conclusion

SwiftUI représente le futur du développement sur l'ensemble des plateformes Apple. Mais aussi le présent, notamment grâce aux points suivants :

  • Sa forte interopérabilité avec UIKit et AppKit lui permet une installation progressive dans nos applications.
  • Il ne doit pas être ignoré, et peut d'ores et déjà s'avérer nécessaire (ex : pour les widgets iOS).
  • Aujourd'hui la rétrocompatibilité n'est souvent plus un problème (iOS 14 min pour la 2.0 alors qu'iOS 16 va bientôt être annoncé)
  • Son paradigme, de par la création déclarative d'interfaces et sa compatibilité avec la programmation réactive, en font un framework moderne et très agréable à utiliser.

Pour autant, faire une application de qualité aujourd'hui 100% SwiftUI est quasi-impossible, en fonction de la complexité des interfaces. La navigation, bien que reprenant les codes existants (navigation stack, presentation sheets), est trop fermée. La création de vues complexes est freinée par ses limites actuelles.

Peut-être faudrait-il finalement se diriger vers une architecture Navigation UIKit / Vues SwiftUI pour prendre le meilleur des deux mondes ?

Auteur

Publié le 30 juin 2022

Ingénieur Logiciel · Mobile