Le pattern d'injection de dépendance est largement utilisé pour appliquer le principe d'inversion de contrôle. Effectivement, ce pattern contribue à une plus grande modularité du code, améliore la maintenabilité et facilite les tests. Toutefois, l'implémentation de l'injection de dépendances peut s'avérer ardue.
Plusieurs frameworks d'injection de dépendances sont disponibles pour Kotlin, parmi lesquels Koin, Dagger-Hilt, Kodein et kotlin-inject se distinguent. Koin, en particulier, semble offrir de nombreux avantages. Explorons pourquoi en le comparant à Hilt, la librairie recommandée par Google pour Android, selon différents critères.
Hilt utilise des annotations qui spécifient les attributs à injecter et la manière de les construire. Bien que Koin supporte également les annotations, il permet aussi la configuration des dépendances via un langage spécifique de domaine (DSL) qui est intuitif et simple à maîtriser. Cette configuration est isolée dans un fichier séparé du code métier, facilitant ainsi la séparation des responsabilités.
Koin et Hilt utilisent des approches différentes pour gérer les dépendances. Koin procède à l'injection à l'exécution en tant que service-locator, instanciant toutes les dépendances et fournissant les références nécessaires aux classes. À l'opposé, Hilt réalise l'injection directement lors de la compilation, et ses annotations sont compatibles avec le Kotlin Symbol Processing (KSP) depuis novembre 2023.
Puisque Koin doit résoudre les dépendances à l'exécution, cela a un impact sur les performances, mais la différence de performance est négligeable.
Par ailleurs, Hilt, par son fonctionnement, détecte les erreurs dès la phase de compilation. Toutefois, Koin qui ne les détecte qu'à l'exécution n’a pas dit son dernier mot, car il est possible de vérifier la configuration de Koin dans des tests unitaires, évitant ainsi le déploiement d'une configuration erronée.
Concernant Kotlin Multiplatform (KMP), Hilt n'est compatible qu'avec Android natif, tandis que Koin est multi-plateformes, offrant un avantage notable. Depuis la Google I/O, il semble que la compatibilité des bibliothèques Google avec KMP soit seulement une question de temps. Reste à voir si Hilt sera facilement adaptable à KMP.
En conclusion, bien que Koin ne soit pas la librairie d'inversion de contrôle préconisée par Google, sa simplicité d'utilisation et sa compatibilité multi-plateforme en font un choix privilégié pour nos nouveaux projets. Pour un projet existant utilisant Hilt, une migration vers Koin devrait être envisagée seulement si un passage à KMP est prévu dans un futur proche.
Dans les projets multi-modules Gradle, la gestion des dépendances et de leurs versions est un défi soulevant trois principaux problèmes. Premièrement, la mise à jour des dépendances doit être effectuée sur chaque module. Deuxièmement, il est difficile de vérifier si une dépendance est obsolète. Enfin, l'autocomplétion des dépendances est inexistante. Créer une solution maison est une option, vous en trouverez plusieurs, mais il est peu probable qu'elles atteignent la qualité du Gradle Version Catalog. Cette dernière crée un catalogue unique et centralisé dans fichier minimal basé sur TOML : vous pouvez y stocker toutes vos dépendances et leurs versions, y compris les plugins. Il est facile de les mettre à jour au besoin.
En effet, le catalogue de versions offre un avantage : votre IDE vous avertit des nouvelles versions de bibliothèques et peut les mettre à jour pour vous. Nous recommandons d'automatiser ces mises à jour avec Dependabot ou une solution similaire.
Tous les modules peuvent référencer les dépendances de manière sûre, permettant à votre IDE de vous aider avec l'autocomplétion, même si vous utilisez encore des fichiers Gradle en Groovy au lieu des nouveaux fichiers KotlinScript.
La migration vers le catalogue de versions est simple et bien documentée. C'est aussi l'occasion idéale de passer vos fichiers Gradle de Groovy vers Kotlin (.kts).
Bien que RefreshVersion soit une autre solution viable, Gradle Version Catalog a été standardisé par Gradle et Google comme solution officielle de gestion des dépendances. Nous recommandons de passer à la solution présentée pour tout projet qui n’utilise pas déjà une des deux solutions viables.
Les développeurs utilisent souvent la génération de code pour éviter les répétitions et améliorer la lisibilité du code. En Android, les annotations sont essentielles. Les développeurs peuvent s'appuyer sur des annotations prédéfinies de librairies comme Room, Hilt ou Glide, ou créer des annotations personnalisées pour générer du code spécifique. Historiquement, le “Kotlin Annotation Processing Tool” (KAPT) était la norme dans la communauté Android. Il permet l'utilisation d'annotations Java dans le code Kotlin, assurant ainsi la compatibilité avec les librairies Java.
Le Kotlin Symbol Processing (KSP), développé par JetBrains et Google, vise à surmonter les limitations de KAPT en traitant directement les annotations Kotlin, sans les convertir en bytecode Java. Cette approche est deux fois plus rapide que KAPT, car elle élimine des étapes de compilation. En outre, KSP s'adapte mieux aux spécificités du langage Kotlin, telles que les paramètres par défaut, les coroutines et les data class.
De nombreuses librairies, auparavant dépendantes de KAPT, migrent désormais vers KSP, la plupart ont déjà fait cette transition. Une aubaine, car KSP prend en charge Kotlin Multiplatform. Les développeurs souhaitant migrer leurs projets d'Android vers Kotlin Multiplatform doivent donc adopter KSP au lieu de KAPT.
Si vous n'utilisez pas d'annotations personnalisées, la migration est simple. Toute librairie nécessitant KAPT fournira des instructions détaillées pour passer à KSP.
Bien que KAPT soit toujours une option viable, KSP le surpasse dans tous les aspects. Il offre un meilleur support pour les fonctionnalités spécifiques à Kotlin et fonctionne deux fois plus vite. Nous recommandons fortement d'adopter KSP.
Un point de frustration récurrent que rencontrent les développeurs avec les images matricielles, comme les PNG, réside dans leur manque de flexibilité : redimensionnement manuel, création de versions multiples pour chaque contexte ou couleur. Les images vectorielles, bien que supérieures, demandent également des manipulations ou des versions spécifiques pour chaque utilisation.
Les SF Symbols d'Apple offrent une solution élégante à ces problèmes. Il s'agit d'icônes vectorielles qui s'intègrent parfaitement avec le texte de notre application, ce qui améliore grandement l'expérience de développement et la maintenabilité. Leur flexibilité est notable : on peut ajuster l'épaisseur du trait, modifier leur couleur et les rendre multicolores, ce qui simplifie le travail des designers et assure une meilleure intégration dans le design system de l'application. De plus, grâce au moteur de rendu partagé avec les typographies, leur performance est optimale.
L'un des avantages majeurs des SF Symbols est leur harmonie avec SwiftUI, notamment en ce qui concerne les animations et l'intégration aux styles hiérarchiques. Pour les développeurs, cela signifie une réduction du temps de développement et pour les utilisateurs, une amélioration de la qualité visuelle des applications.
Il est possible de créer ses propres SF Symbols avec l'outil Sketch directement ou en partant d'une base existante. Cependant, la gestion des marges n'étant pas la même par rapport à celle des SVG, il est préférable d'utiliser un convertisseur spécialisé comme SfSymbolConverter, surtout si vous avez un set d'icônes déjà existant. Alternativement, les 6 000 icônes disponibles par défaut offrent une solution rapide et efficace pour réduire le lead time des fonctionnalités.
Nous vous recommandons l'adoption de cette technologie pour tous vos projets natifs. Malgré quelques subtilités dans la création de symboles personnalisés, les SF Symbols présentent des avantages indéniables en termes de rapidité d'intégration, de confort, de flexibilité et de performance.
Swift, malgré ses avantages, souffre de problèmes affectant la productivité des développeurs. D'une part, un problème majeur de lenteur de compilation, particulièrement prononcé dans les projets de grande taille, où le typage provoque des échecs du compilateur, avec le message « The compiler is unable to type-check this expression in reasonable time ». D'autre part, la validité du code concurrent est déléguée aux développeurs qui l'écrivent.
Swift 6 propose une solution à ces problèmes. Outre une gamme de nouvelles fonctionnalités et de syntaxes, l'une des améliorations les plus significatives de cette nouvelle version est sa vitesse de compilation accrue. Cette amélioration à elle seule justifie largement la migration, car elle peut considérablement réduire la frustration des développeurs et améliorer leur productivité.
Mais Swift 6 met également en avant le code concurrent vérifié par le compilateur (statiquement), ce qui permet aux développeurs de réduire le nombre de ces bugs parfois difficiles à détecter. Cependant, cette fonctionnalité n'est pas sans inconvénients. Cette validation statique implique des changements majeurs qui perturberont les bases de code existantes, nécessitant ainsi des efforts significatifs de refactoring et d'adaptation. Cette transition peut être difficile et coûteuse en ressources, en particulier pour les projets de grande envergure, malgré la migration progressive rendue possible par les feature flags disponibles en Swift 5.
Compte tenu de ces considérations, nous recommandons de migrer vers Swift 6. Bien que les changements majeurs nécessitent une évaluation attentive de l'investissement, les avantages de la vitesse de compilation améliorée et de la concurrence vérifiée sont substantiels.
SwiftUI offre des primitives de code puissantes pour externaliser le state, mais nécessitent une architecture bien pensée pour tirer pleinement parti de leur potentiel et ne pas créer de gigantesques states monolithiques. De plus, avec une communauté de développeurs fracturée entre UIKit, SwiftUI, les @Observable, les @ObservableObject, différentes architectures, il n'est pas facile de trouver ses marques dans un projet existant.
“The Composable Architecture” (TCA) est un framework conçu pour résoudre ces problèmes dans les applications iOS. Inspiré de l’architecture unidirectionnelle Flux popularisée par Redux, TCA adapte cette approche à Swift, rendant la gestion de l’état plus intuitive et moins verbeuse qu'en JavaScript. Contrairement à des bibliothèques comme ReSwift, TCA est conçu pour SwiftUI, tout en restant compatible avec UIKit.
TCA structure le state autour des actions des utilisateurs, assurant un flux de changements traçable et clair, ce qui facilite les tests et permet une architecture modulaire. Les fonctionnalités peuvent être développées indépendamment, puis intégrées pour former une application complète. Cette formalisation du code améliore l’expérience de développement et réduit la courbe d’apprentissage. En outre, la communauté autour de TCA est robuste et offre une documentation exhaustive et des tutoriels pour accompagner les développeurs.
L'arrivée de la gestion de la navigation, ainsi que le "back-port" de certaines fonctionnalités autrement indisponibles sur des versions antérieures d'iOS, comme l'observabilité du state, témoignent de la transformation de la librairie cette année, la propulsant au sommet de son domaine.
Chez Theodo, nous avons mis en œuvre The Composable Architecture avec succès dans des projets en production et nous le recommandons vivement pour la gestion de state dans les applications SwiftUI.
Nous avons déjà adopté Tuist dans notre précédent Tech Radar en raison de ses capacités à améliorer la gestion des projets iOS. Son utilisation simplifie la configuration des builds. Une meilleure gestion du cache des builds permet l'accélération les temps de compilation, en particulier pour les projets basés sur une architecture modulaire. Cependant, en raison des changements majeurs introduits, cette nouvelle version peut s'avérer clivante.
Le premier changement majeur est l’arrêt de la prise en charge de Carthage : chaque projet devra désormais gérer le fetch des dépendances Carthage, ce qui peut nécessiter des ajustements significatifs dans un workflow existant.
De plus, Tuist 4 ne gère plus la signature des applications, obligeant ainsi les développeurs qui utilisaient cette fonctionnalité à revoir entièrement leur workflow de signature.
Une autre nouveauté de Tuist 4 est l’utilisation des fichiers de SPM (Swift Package Manager) plutôt qu’un format spécial. Cette transition permet de mieux s’intégrer dans les outils de l’écosystème (par exemple Xcode ou dependabot).
Cette version est peut-être en réalité celle de la maturité, car Tuist se concentre sur ce qu’il fait bien afin de le faire mieux. Enfin, les défis cités sont fort heureusement facilités grâce à une documentation de migration fournie ainsi qu'un bon support de la communauté ; cela permet de profiter des améliorations significatives en termes de performance de build apportées par Tuist 4. La syntaxe est également encore plus intuitive, ce qui simplifie l’utilisation de l’outil et accélère le développement.
Nous vous recommandons toujours d'adopter Tuist pour de nouveaux projets. Nous vous recommandons également la migration vers Tuist 4. Les avantages à long terme en termes de performance et de gestion des projets surpassent largement les défis initiaux de la transition.
La complexité croissante des applications mobiles pose des défis, notamment sur la maintenabilité d'une base de code grandissante, le temps de build croissant et les difficultés à respecter la pyramide des tests. Pour y remédier, des entreprises comme SoundCloud et JustEat ont popularisé l'architecture micro-features, qui divise le monolithe en modules plus petits et plus spécialisés.
Cette architecture se compose de quatre types de modules :
L’architecture micro-features offre des avantages notables : elle facilite les tests des logiques métiers, réduit le temps de build grâce au cache, et permet de mutualiser le code commun lors de l’agrandissement d’équipe ou de développement de nouveaux produits. Cette approche simplifie également les évolutions et les refontes partielles d’applications complexes.
Cependant, sa mise en œuvre requiert une bonne expertise du domaine pour un découpage efficace, une solide compréhension des principes d’architecture logicielle, et une bonne conception initiale. Des outils comme Tuist pour iOS, peuvent faciliter ce découpage, améliorer le cache, et rendre la transition vers cette architecture plus fluide.
Chez Theodo, les micro-features sont devenues un choix de référence dans les nouveaux projets natifs. Nous avons pu tester à quel point cette architecture permettait de simplifier les évolutions ou refonte partielle d'applications complexes.
Avec Jetpack Compose, lorsque le paramètre d’un composable change, seule la partie impactée est mise à jour, à condition que le reste soit stable. Si un paramètre est considéré instable, toute la vue sera re-rendue même si sa valeur n'a pas changée, cela peut ralentir le rendu et augmenter la charge du thread UI, affectant les performances des écrans complexes.
Un problème majeur est que toute classe provenant d'un module différent est considérée comme instable par le compilateur Compose. Les classes modèles sont souvent séparées des vues pour respecter la séparation des responsabilités et cela dégrade la stabilité des composables. Vous pouvez détecter ces problèmes de stabilité avec des outils comme le layout inspector ou le compose compiler report.
Pour résoudre cela, trois stratégies principales ont été utilisées :
Jetpack Compose a récemment introduit un fichier de configuration de stabilité, une approche déclarative pour gérer la stabilité sans modifier le code. Ce fichier permet au compilateur Compose de traiter les classes listées comme stables.
L'utilisation de ce fichier est plus rapide, élégante et moins contraignante que les autres solutions. Les développeurs peuvent configurer des classes stables dans un fichier à la racine ou dans des fichiers séparés pour chaque module.
Chez Theodo, nous voyons un grand potentiel dans cette solution. Bien qu’elle soit nouvelle et puisse avoir des limites encore inconnues, nous recommandons une implémentation progressive pour assurer la compatibilité des projets.
Kotlin Multiplatform (KMP) s'attaque à un problème central du développement moderne : éviter la duplication de code et les coûts associés, tels que la multiplication des bugs et les incohérences entre les plateformes.
Il existe de nombreuses solutions multiplateformes sur le marché, comme React Native ou Flutter. Ces solutions sont des frameworks qui offrent une base de code unique pour créer des applications complètes qui ciblent plusieurs plateformes.
KMP adopte une approche plus flexible. Ce n'est pas un framework, mais une technologie permettant au langage Kotlin de compiler sur d'autres plateformes que la JVM utilisée pour le développement natif Android.
Cette approche, plus modulaire, rend KMP idéal pour mutualiser du code entre différentes plateformes (sous forme d'une librairie) sans imposer quoique ce soit.
Sur iOS, par exemple, le code Kotlin est compilé et génère une librairie utilisable dans des projets iOS exactement comme une autre librairie native de cette plateforme. Cette flexibilité permet de partager du code selon les besoins : une simple fonction, une couche spécifique de l'application (réseau, métier, etc.), une fonctionnalité spécifique, ou même l'intégralité du code. On partage uniquement ce que l'on veut partager.
Grâce à KMP, Kotlin bénéficie d'une interopérabilité bidirectionnelle : on peut compiler du code Kotlin et l'intégrer dans du code natif, tout en utilisant des bibliothèques natives existantes dans du code Kotlin spécifique à chaque plateforme, simplifiant ainsi les ponts souvent nécessaires pour exploiter les fonctionnalités propres à chaque plateforme.
L'écosystème de KMP est en plein essor, avec Google travaillant activement à rendre ses bibliothèques natives Android compatibles avec KMP. D'un autre côté, la communauté est également très active, et les bibliothèques les plus populaires de l'univers Android sont déjà compatibles avec KMP ou très proches de l'être, telles que Room, Retrofit, Coil, Koin, etc.
KMP est désormais stable sur presque toutes les plateformes, y compris Android, iOS, Desktop (Windows, Mac, Linux), et même le web en transpilant vers JavaScript/TypeScript. La compilation vers le WebAssembly est quant à elle encore en alpha. De plus, la compilation pour iOS se fait pour l'instant vers de l'Objective-C bien que Jetbrains ait annoncé l'arrivée prochaine de Kotlin-To-Swift lors de la Kotlin Conf 2024 à Copenhague.
KMP s'impose comme une solution idéale pour partager du code entre différentes plateformes. Nous vous encourageons à l'essayer : ajouter un module KMP à un projet Android est assez simple (quelques configurations dans Gradle suffisent) et vous pouvez commencer à partager du code. KMP est parfait pour créer une bibliothèque multiplateforme. Cependant, si vous souhaitez également partager l'interface utilisateur, il faudra utiliser Compose Multiplatform (CMP), qui n'est pas encore aussi stable que KMP.
Nous vous encourageons à démarrer vos nouveaux projets Android directement avec KMP pour garantir leur évolutivité. Cela n'affecte en rien le projet lui-même, mais vous permettra de partager du code plus facilement à l'avenir.
Dans le domaine du développement, il est souvent difficile de rendre le code testable et de maintenir une base de code évolutive en raison du fort couplage des dépendances. Une solution efficace pour résoudre ce problème est l'injection de dépendances, qui permet de découpler les composants du code et d'améliorer ainsi sa testabilité et sa maintenabilité.
Factory est une bibliothèque moderne qui résout ces problèmes de manière efficace pour les développeurs Swift. Elle offre des avantages clés tels que la vérification de l'injection des dépendances au moment de la compilation, assurant ainsi qu'aucune erreur ne survienne à l'exécution en raison d'une dépendance manquante ou incorrectement injectée. Factory permet également l'utilisation de scopes d'injections, facilitant ainsi la mise en place d'une véritable inversion de contrôle grâce au pattern Dependency Injection.
L'année dernière, nous recommandions de conserver Resolver pour les projets utilisant UIKit ; cependant, en raison de sa dépréciation, nous préconisons désormais de changer de bibliothèque d'injection de dépendances. Pour ceux qui souhaitent conserver une syntaxe similaire et plus de fonctionnalités, Factory est une excellente alternative. Du fait de la proximité de sa syntaxe et de son fonctionnement, la migration de Resolver vers Factory se fait facilement. La documentation de Factory est également améliorée, offrant des instructions claires et détaillées pour une utilisation efficace dans les projets SwiftUI et UIKit.
Nous recommandons d’essayer Factory dans vos projets car elle présente un potentiel significatif pour améliorer la qualité du code et la performance des applications iOS. Bien que des compromis existent, ses avantages justifient une évaluation sérieuse pour les projets futurs.
Le stockage des données dans les applications mobiles est essentiel pour améliorer l’expérience utilisateur et éviter les temps d’attente. Un système de cache permet de fonctionner en mode offline-first, en affichant d’abord les données en cache avant de les rafraîchir via un appel réseau.
SQLite est la solution standard pour les bases relationnelles sur mobile, mais il est souvent préférable d’utiliser un ORM (Object-Relational Mapping), une librairie faisant l'interface entre une base de données et le reste du code, pour simplifier les interactions et le typage.
Dans le contexte d'Android, Google a créé et intégré Room dans Android Jetpack, un ensemble de bibliothèques visant à simplifier la tâche des développeurs. Avec Room, il est possible de déclarer facilement toute la structure de la base de données et les différentes requêtes en quelques lignes de code Kotlin, avec des annotations.
Room est une librairie mature, existant depuis 2018, qui bénéficie d'une documentation complète et est facile à utiliser. Elle s'intègre facilement en Kotlin et offre la possibilité de faire des opérations réactives. Par exemple, lorsqu'une table est modifiée (ajout, modification ou suppression de lignes), Room émet automatiquement une nouvelle valeur, permettant aux composants de se mettre à jour via des méthodes réactives comme des flows (coroutines), des observables (Rx) ou des Livedata.
La nouveauté en 2024 est que, depuis mai, Room est compatible avec Kotlin Multiplatform. Bien que cette version soit encore en alpha, Google a annoncé son engagement envers KMP, ce qui laisse présager une version stable avant la fin de l'année.
Il existe des concurrents sérieux à Room, comme SQL Delight ou Realm, qui offrent également une compatibilité avec KMP. Quoiqu'il en soit, Room demeure relativement simple à utiliser : c'est une valeur sûre pour Android et bientôt pour KMP aussi. Vous pouvez l'intégrer sans risque à votre application Android et envisager son utilisation dans une future version Multiplatform.
Dans le développement iOS, le découplage des dépendances est un défi récurrent, car aucune solution officielle n'existe. Les projets ont donc souvent un code fortement couplé, rendant les tests et la maintenance difficiles.
Swift Dependencies est une librairie d'injection de dépendances conçue pour solutionner ces problèmes dans les applications Swift. Initialement pensée pour The Composable Architecture (TCA), il est tout à fait possible de l'utiliser de façon autonome.
Comme avec tous les mécanismes d'injection de dépendances, découpler votre code de ses dépendances et permettre d'injecter des mocks pendant les tests le rend plus facilement testable et maintenable. De plus, elle fournit une méthode pour injecter des stubs pour les previews.
Cette librairie utilise des mécanismes similaires à @Environment de SwiftUI pour la gestion des dépendances, ce qui facilite son apprentissage. Elle inclut également une macro pour implémenter rapidement la plupart des dépendances, accélérant leur définition et réduisant le code boilerplate.
Swift Dependencies est légère et compatible avec une adoption progressive, permettant aux équipes de l'intégrer à leur propre rythme. Cependant, cette légèreté se fait au prix de certaines fonctionnalités comme les weak injections et les scopes d'injection, qui doivent être réimplémentées au cas par cas.
Swift Dependencies offre donc une manière convaincante gérer les dépendances, que ce soit dans un projet TCA ou en migration incrémentale dans un projet sans injection, même si l'absence de fonctionnalités importantes peut être un frein à son adoption.
En programmation, on distingue les erreurs métier (un mot de passe trop court) des erreurs techniques (une requête réseau échouée). Les erreurs métier sont des cas normaux d'exécution et doivent être gérées au même titre que les succès, tandis que les erreurs techniques sont des détails d'implémentation.
Historiquement, Java utilise des exceptions vérifiées (checked) obligatoires. Mais leur gestion est difficile et elles polluent souvent le code métier, le rendant dépendant de l'implémentation. De plus, elles sont régulièrement dévoyées pour gérer les erreurs métier, ce qui est coûteux en termes de performance car elles génèrent une stacktrace.
Kotlin a répondu à ce problème avec des exceptions non vérifiées, rendant la gestion des erreurs optionnelle. Cela réduit la robustesse du code mais permet de découpler le code métier de l’implémentation technique.
Arrow, une bibliothèque pour Kotlin, propose une approche inspirée de la programmation fonctionnelle pour gérer les erreurs métier : les Typed Errors. Cela permet de distinguer les erreurs techniques, gérées par les exceptions non vérifiées, des erreurs métier, avec deux méthodes principales :
Ces méthodes évitent try-catch et les stacktraces, améliorant ainsi performance et flexibilité.
Arrow est un outil puissant pour rendre le code Kotlin plus robuste et lisible. Either est déjà incontournable, mais Raise, bien que prometteur, reste limité par sa dépendance à des fonctionnalités expérimentales.
La configuration Gradle est incontournable pour tout projet Android ou Kotlin Multiplatform, mais elle peut rapidement devenir complexe si l'on sort des sentiers battus. C'est notamment le cas avec des projets multi-modules, des flavors variées, et l’intégration de plugins tiers. Cette complexité, souvent source de frustration, nécessite une courbe d’apprentissage raide, même pour les développeurs expérimentés.
JetBrains a pris en compte ces défis et a développé Amper, un nouveau système de build conçu pour simplifier la configuration. Intuitif et puissant, Amper s’intègre parfaitement à l’écosystème existant. Il existe en version standalone pour les projets simples et en tant que plugin Gradle pour ceux qui nécessitent l’écosystème de Gradle.
Amper simplifie la configuration des projets Kotlin Multiplatform avec un fichier par module, réduisant ainsi le boilerplate de Gradle. En tant que plugin pour Gradle, Amper est totalement interopérable, offrant un fallback Gradle pour toute fonctionnalité non supportée.
Amper supporte la création et l'exécution d'applications JVM, Android, iOS, Desktop et Web, ainsi que la création de bibliothèques Kotlin Multiplatform. Il permet également de mixer du code Kotlin, Java et Swift, tout en supportant les projets multi-modules et l'utilisation de Compose Multiplatform.
Cependant, Amper est encore en phase de preview et n’est pas recommandé pour les projets en production : il est encore jeune, avec une API susceptible de changer et des fonctionnalités manquantes. C’est néanmoins le bon moment pour expérimenter et fournir des retours à JetBrains, reconnus pour leur écoute des utilisateurs.
Amper est spécifiquement conçu pour faciliter le développement de projets Kotlin Multiplatform et pourrait devenir une solution intéressante à explorer pour les développeurs souhaitant simplifier la gestion et la maintenance de leurs builds.
Kotlin Multiplatform (KMP) permet de partager du code écrit en Kotlin entre diverses plateformes (Android, iOS, Desktop, Web, etc.) mais pas de faire des interfaces utilisateurs. Ainsi, même en partageant la logique métier, il est nécessaire d'utiliser des solutions natives (comme Compose sur Android ou SwiftUI pour iOS).
Pour remédier à cela, JetBrains développe Compose Multiplatform (CMP) qui permet d'écrire une UI commune à toutes les plateformes en utilisant l'API de Compose éprouvée sur Android et le moteur de rendu Skia éprouvé par Flutter.
CMP est une technologie très prometteuse, mais elle est encore jeune et les niveaux de stabilité varient selon les plateformes :
C'est la raison pour laquelle JetBrains la déprécie en faveur de sa remplaçante : la version compilée vers WebAssembly. Cependant, celle-ci requiert des versions très récentes des navigateurs et n'est pas encore compatible avec Safari. Les deux solutions nuisent au référencement (SEO) et, étant encore en phase alpha, sont particulièrement instables et inadaptées à un projet destiné au grand public.
CMP est très prometteur et nous croyons en son potentiel : pour les projets ciblant Android et Desktop, il peut être adopté sans hésitation. Pour iOS, il est également recommandable malgré son statut bêta. Cependant, pour le web, il est encore trop tôt pour utiliser CMP en production à large échelle. Les progrès rapides réalisés par JetBrains et la communauté sont encourageants et c'est pourquoi nous surveillons cette technologie de près.
L’annotation @Observable, introduite avec iOS 17, est une avancée majeure pour la réactivité des applications iOS. Cependant, cette fonctionnalité n’est disponible que sur iOS 17 et au-delà. Cela pose un problème pour les développeurs qui doivent maintenir la compatibilité avec des versions antérieures d’iOS (iOS 14 à 16), les privant ainsi de cette fonctionnalité essentielle.
La librairie swift-perception vise à résoudre ce problème en backportant @Observable pour les versions d'iOS 14 à 16 rendant son utilisation possible sans nécessiter de mise à jour de l’OS des utilisateurs ou la création de plus de déchets électroniques.
Cependant, l’utilisation de swift-perception présente quelques inconvénients. D’abord, bien que cette solution émule le comportement de @Observable, elle n'est pas aussi bien intégrée que l’implémentation native. La syntaxe fournie est similaire, mais pas identique, à @Observable : il est nécessaire d'envelopper vos Views dans un composant spécifique, ce qui ajoute du bruit dans le code. Celui-ci devient moins agréable à relire et cela augmente la charge de travail pour supprimer la librairie si elle devient obsolète.
En outre, comme toutes les librairies utilisant les macros avancées de Swift, swift-perception allonge considérablement le temps de compilation. Il existe des solutions pour mitiger ce problème, comme la précompilation de swift-syntax, mais le cœur du problème demeure.
En conclusion, swift-perception est une solution précieuse pour les développeurs souhaitant utiliser @Observable sur des versions d’iOS antérieures à 17. Malgré ses inconvénients, cela permet de préparer le code pour le futur sans se priver d'utilisateurs. Chez Theodo, nous l'intégrons dans notre R&D et nous vous recommandons de l'essayer pour vos projets compatibles avec iOS 14 à 16.
XCTest a incarné les tests iOS depuis qu'il a remplacé OCUnit, mais il est connu pour sa verbosité et son manque de flexibilité. En effet, le code de test croît exponentiellement en quantité et n'est pas facilement lisible. Cela a poussé les développeurs à chercher des alternatives comme Quick. Mais bien que ces outils aient une syntaxe plus expressive, ils ne répondent pas au problème de la verbosité, tout en imposant quand même un apprentissage plus long.
Swift Testing apparaît prometteur face à ces défis. Ce framework propose une syntaxe qui tire parti de la puissance des macros et des annotations, afin d'être plus intuitif et moins verbeux. On notera par exemple la paramétrisation des tests, permettant aux développeurs d'exécuter le même test avec différentes entrées de manière transparente.
De plus, Swift Testing améliore l'organisation des tests grâce à une hiérarchie basée sur les types Swift et une catégorisation basée sur des tags. Il exploite également Swift 6 pour améliorer la sûreté des tests concurrents mais surtout pour lancer les tests en parallèle par défaut, ce qui améliore grandement leur vitesse d'exécution.
Enfin, l'intégration aux côtés de tests existants basés sur XCTest est simple, permettant une migration facile sans revoir toute l'infrastructure de test.
Chez Theodo, nous surveillons de près Swift Testing. Son approche innovante et ses fonctionnalités en font un candidat sérieux pour une adoption future. Cependant, étant donné sa nouveauté, il est actuellement en phase d'assess. Nous pensons que Swift Testing a le potentiel de devenir un outil clé dans notre stratégie de test à mesure qu'il prouve sa fiabilité et s'enrichit de fonctionalités (comme les tests UI ou de performances).
Le hot reload est la capacité de voir les changements apportés à une application en cours d'exécution sans avoir à attendre sa recompilation. Cette fonctionnalité accélère le développement en permettant aux développeurs de constater les effets de leurs modifications en temps réel et dans un contexte plus proche du réel que les previews.
Cette capacité est généralement considérée comme inaccessible aux technologies compilées comme SwiftUI. Cependant, une librairie permet de rendre cela possible en recompilant puis en injectant le code modifié grâce à des outils très bas niveau : InjectIII. Cette librairie prend également en charge UIKit, Vapor et même Bezel.
Cependant, l'utilisation d'InjectIII implique de lourdes concessions. La première installation peut être longue et frustrante. Par la suite, il faut faire preuve de beaucoup de patience face aux limitations techniques : il peut être nécessaire de changer le chemin vers votre application ou de compromettre la sécurité de votre Mac pour activer le hot reload. De plus, certaines fonctionnalités de SwiftUI, comme .onChange, ne sont pas compatibles avec le rechargement.
Le principal inconvénient de cette librairie réside toutefois dans la nécessité de modifier tous les composants de votre codebase pour effacer leurs types vers AnyView. Ces adaptations considérables peuvent interférer avec les mécanismes d'identification SwiftUI, affectant les animations et les performances de votre application lors du développement. Il faut également un workflow spécifique pour retirer toute trace de la librairie en production, sans quoi la sécurité et les performances de l'application seraient compromises.
En dépit de l'exploit technique réalisé, nous déconseillons l'utilisation de cette librairie en raison de ses coûts élevés. Néanmoins, cela démontre qu'il est possible de mettre en œuvre le hot reload et certains ingénieurs chez Apple pourraient s'en inspirer.
Retrouvez l'avis de nos experts sur les techniques, plateformes, outils, langages et frameworks associés aux principales technologies mobiles que nous utilisons au quotidien chez BAM : React Native, Flutter et Native.