Retour d’expérience sur Kotlin Multiplatform Mobile (KMM)

KMM ou Kotlin Multiplatform Mobile est un outil de développement crossplatform pour applications mobiles offrant une nouvelle approche au développement hybride en prenant le parti pris de mutualiser les logiques métiers et de conserver un développement natif pour la partie graphique. Encore en version Alpha aujourd’hui, le site officiel référence déjà Netflix, Philips, Autodesk ou encore VM Ware parmi ses utilisateurs. Comme toute technologie hybride il est possible développer des briques communes mais également des briques spécifiques à chaque plateforme comme nous le verrons dans la suite de l’article.

Cet article nécessite des connaissances en Gradle et Kotlin ainsi que quelques bases avec les outils de l’écosystème iOS tels que Cocoapods.

L’objectif de cet article n’est pas de reprendre la documentation officielle mais de vous présenter la solution mise en place chez Sedona afin de pouvoir livrer une librairie développée via KMM qui sera ensuite intégrée au sein de deux applications natives Android et iOS. Afin de favoriser une bonne adoption et intégration au sein des habitudes et des outils de chacune des deux plateformes, la librairie sera mise à disposition via Artifactory pour Android et Cocoapods pour iOS.

À la différence de ce qui est présenté dans la documentation officielle, nous n’utiliserons pas KMM pour compiler les applications mais uniquement pour compiler une librairie. En effet, nous avons souhaité intégrer KMM dans une application déjà existante et limiter l’impact sur la toolchain en place au minimum, c’est-à-dire au simple ajout d’une dépendance.

Initialisation du projet

Tout d’abord veuillez suivre les instructions de la documentation officielle de KMM pour créer votre nouveau projet (anglais).

La structure de votre projet devrait ressembler à celle présente sur la capture ci-contre (à la différence que shared a été renommé libutils dans notre cas).

  • androidApp : contient le code source de l’application Android ;
  • iosApp : contient le code source de l’application iOS ;
  • libutils : contient le code source de notre librairie qui sera partagée entre iOS et Android.
Arborescence par défaut d’un projet KMM

Au sein de votre dossier shared vous trouverez les répertoires suivants :

  • androidMain et iosMain : contiennent le source spécifique à chaque plateforme ;
  • commonMain : contient le code source partagé entre les différentes plateformes.

Chaque source est accompagnée d’un dossier destiné à recevoir les tests associés.

Passage à la pratique  

Préambule

KMM introduit deux nouveaux mots clefs :

  • expect : utilisé dans les sources appartenant au commonMain, ce mot clé permet d’indiquer au compilateur qu’une implémentation spécifique à une plateforme est disponible dans le dossier correspondant ;
  • actual : utilisé dans les sources spécifiques à chaque plateforme afin de fournir l’implémentation spécifique à la plateforme.

D’un point de vue conceptuel, on peut assimiler le mot clef expect à une abstract. Ces deux nouveaux mots clefs permettront de réaliser des implémentations spécifiques par plateforme comme par exemple une écriture disque ou l’utilisation d’une librairie spécifique à un OS, nous y reviendrons dans la suite de l’article.

Ajout des dépendances

Dans notre projet nous avons utilisé les librairies Storage et RemoteConfig de Firebase avec une implémentation spécifique à chaque plateforme en fonction des besoins.

Tout d’abord il faut ajouter les dépendances suivantes pour Android dans le fichier gradle présent dans le dossier shared/build.gradle.kts :

Pour iOS, référez-vous à la documentation officielle du plugin Cocoapods (anglais) et ajoutez ceci à votre fichier shared/build.gradle.kts :

Lors du gradle sync, un fichier .podspec sera généré dans le dossier iosApp. Il s’agit d’un fichier décrivant le contenu de votre librairie et donnant un ensemble d’instructions à Cocoapods afin de permettre la configuration du projet Xcode. Pour plus d’info sur le format du fichier .podspec vous pouvez vous consulter la documentation sur le site officiel (anglais).

Implémentation

Voici un exemple d’utilisation du mot clef expect que nous plaçons dans le dossier commonMain :

La contrepartie en natif côté Android utilisant le mot clef actual :

Et la contrepartie en natif côté iOS :

Comme vous le constaterez dans les portions de codes spécifiques à chaque plateforme, les implémentations utilisent des APIs propres à l’OS correspondant, comme le NSFileManager sur iOS.

L’exemple met en avant une implémentation spécifique car l’écriture sur le disque n’est pas possible autrement, l’avantage de KMM étant de mutualiser l’intelligence de votre application dans une brique commune, il convient d’étudier la part de développement spécifique à chaque plateforme avant de recourir à cette technologie. La portion de code spécifique dans le projet utilisé pour illustrer cet article ne représente que 5% du code de la librairie.

Publication de la librairie

Pour Android, la librairie est publiée au format .aar via un repo Artifactory privé.

Pour iOS, la librairie est commit / tag / push au format binaire dans un repo dédié puis publiée dans un repo privé Cocoapods. Afin de garantir que le binaire sera compatible avec le simulateur et les appareils physiques il est nécessaire de compiler la librairie pour les deux jeux d’architectures de processeur (ARM et x86) puis de les fusionner au sein d’un « fat bin » via la commande lipo. Je vous recommande de suivre la documentation officielle sur la création d’un repo Cocoapods privé (anglais) si vous souhaitez plus d’information sur le sujet. Suivez cet article pour obtenir un « fat bin » ou XCFramework (anglais).

Retour d’expérience

Lors du build

  1. Le principal problème rencontré avec KMM est bien entendu son statut de version alpha, il en a résulté de nombreux problèmes d’instabilité de gradle et de nombreuses incompatibilités de plugins Android Studio.
  2. Lors de l’ajout de dépendances spécifiques à une plateforme (principalement pour iOS), le gradle sync et la compilation prenaient de plus en plus de temps, parfois jusqu’à 30 minutes pour exécuter un gradle sync.

Pendant le développement

  1. En tant que développeur Android, j’ai éprouvé quelques difficultés à développer les parties de code spécifique à iOS car je n’étais pas familier des APIs notamment pour les écritures de fichiers.
  2. Le fait d’utiliser une base de code commun m’a forcé à abandonner certaines librairies que j’apprécie et avec lesquelles j’étais particulièrement familier comme Retrofit ou bien Picasso.

Durant la publication

Après l’étape de configuration de Cocoapods dans le projet, la génération d’un « fat bin » fut beaucoup plus complexe que prévu et j’ai passé beaucoup de temps à expérimenter la commande lipo manuellement.

Conclusion

De mon point de vue KMM va devenir l’un des outils les plus populaires, non pas pour développer des applications crossplatform, mais pour développer des librairies crossplatform permettant de mutualiser les règles métier.

Avantages

  • Même langage de développement ;
  • Gain de temps à long terme ;
  • Le code peut être maintenu par un développeur Android sans ou avec très peu de connaissances en développement iOS.

Inconvénients

  • Le temps d’exécution des gradle sync ;
  • Plus accès aux librairies populaires de chacun des plateformes (Retrofit, Picasse, Alamofire, …) ;
  • Le multithread ne semble pas bridgé correctement sur les deux OS ;
  • Montée en compétence abrupte au début ;
  • Les outils de déboggage sur iOS gagneraient à être améliorés.

Cette première prise en main de l’outil fut très intéressante et nous pensons continuer à l’utiliser même s’il s’agit encore d’une version alpha.

You may also like...