Vers un nouveau modèle de mutualisation d’application mobile avec Kotlin/Native

Le partage de code entre différentes plateformes est devenu une pratique de plus en plus utilisée dans le monde du développement mobile, que ce soit React Native avec Facebook ou Flutter avec Google, toutes les entreprises IT s’y mettent et y trouvent toutes un certain intérêt.

Malgré la multitude de technologies déjà disponibles sur le marché, la solution magique adaptée à tous besoins n’existe pas, d’autant plus quand il s’agit de mixer du code natif avec du code hybride.

Depuis quelques années, le langage Kotlin est devenu de plus en plus utilisé dans le développement d’applications Android et son créateur JetBrains a poussé son développement afin de l’utiliser dans différentes plateformes via Kotlin/Native.

Dans cet article nous allons voir ce qu’est Kotlin/Native, comment l’utiliser et quels sont les avantages et inconvénients de cette technologie.

Qu’est ce que Kotlin/Native?

Kotlin/Native est un framework qui permet de générer des binaires natifs. Sa spécificité est de mutualiser du code en utilisant Kotlin comme langage source et à travers une phase de transpilation, de créer des bibliothèques natives selon les plateformes ciblées, iOS et Android dans notre cas.

Choix d’environnement de développement et configuration

Il existe plusieurs IDE permettant d’utiliser Kotlin/Native tels que AppCode ou Clion mais ils n’ajoutent aucun confort par rapport aux environnements traditionnels. Il est donc plus simple d’utiliser Android Studio du fait qu’il soit aussi utilisé pour développer des applications Android et Xcode pour le développement iOS.

Le site officiel Kotlinlang explique bien les étapes de configuration pour les deux plateformes et pour résumer, l’architecture projet obtenue est la suivante :

androidApp et iosApp : sont utilisés pour le développement des applications natives des différentes plateformes.

SharedCode : concerne le module pour le développement de la bibliothèque qui sera partagée et est divisé en plusieurs parties :

  • commonMain : pour le code commun entre iOS et Android
  • commonTest : pour implémenter les tests de la partie commune
  • androidMain et iosMain : servant de build type pour écrire du code spécifique à une plateforme, on verra par la suite les détails concernant cette partie

Que mutualiser?

Beaucoup d’applications mobiles utilisent les notions suivantes :

  • La logique métier
  • Les appels réseaux
  • Le stockage en base de données
  • Les injections de dépendances

Mutualiser toutes ces parties nous laisse principalement l’interface graphique qui est codée nativement dans chaque plateforme. Mais on peut aller plus loin en introduisant une architecture telle que Viper ou la clean architecture afin de mieux organiser notre bibliothèque. Toutes les notions mutualisables seront réparties dans les différentes composantes de l’architecture choisie.

Aujourd'hui il existe des bibliothèques hybrides telles que Ktor pour les appels réseaux ou Kodein pour les injections de dépendances permettant de faciliter la mutualisation de code mais dans certains cas nous sommes hélas dépendant de l’implémentation native de certaines fonctionnalités telles que l’affichage de log ou encore, plus complexe, les notifications push.

Appel à des fonctionnalités natives

Une des forces de Kotlin/Native c’est de facilement appeler du code natif via notre bibliothèque et cela grâce au mécanisme Actual et Expect.

Expect est un mot clé qui sera utilisé à côté d’une déclaration de fonction ou de variable se trouvant dans le dossier commonMain et qui fait appel à son implémentation se trouvant dans iosMain et androidMain dont le nom est le même et qui contient le mot clé Actual.

La bibliothèque générée ne comprendra qu’une des deux implémentation selon la plateforme ciblée. Encore une fois le site officiel Kotlinlang explique bien la démarche à suivre avec des exemples de code plus poussés.

Mon expérimentation avec ce framework

Après m’être formé sur cette technologie j’ai pu réaliser une application permettant de convertir une monnaie d’une devise à une autre en ayant pour objectif de mutualiser tout son fonctionnement.

Sur ce prototype j’ai pu tester :

  • L’appel vers une API externe avec Ktor
  • Les injections de dépendances avec Kodein
  • La sauvegarde en base de données de l’historique de conversion avec SqlDelight
  • Les tests unitaires avec MocK

Le résultat en terme d’architecture est le suivant en utilisant une variante de la clean architecture :

Pour un développeur Android/Kotlin, une fois la connaissance des particularités du framework appréhendée, la création de la bibliothèque reste une "continuation" de ses habitudes de développement. Par contre, pour un développeur iOS sans expérience sur Kotlin, il peut y avoir une complexité plus grande liée à l'utilisation d'un nouveau langage et à un autre IDE.

Quelques désagréments

Après avoir expérimenté cette technologie pendant un certain temps j’ai pu constater 3 principaux désagréments :

  1. En passant par Kotlin, le framework génère une bibliothèque en Swift/Objective C en ce qui concerne iOS et la conversion peut parfois nous jouer des tours. un Long en Kotlin se traduit en Int64 en Objective C, autre exemples :  
  2. Débugger une application qui utilise la bibliothèque générée en dehors d’Android Studio n’est pas encore au point. Pour un développeur iOS il est nécessaire de changer d’IDE et d’utiliser par exemple AppCode pour pouvoir utiliser des breakpoints qui est une fonctionnalité basique de débug néanmoins accéder à une variable à un instant T n’est pas encore possible.
  3. Certaines bibliothèques externes dont le fonctionnement n'est pas le même sur les deux plateformes telles que les coroutines.

À noter que le framework est en pleine évolution et qu’à un certain moment, la phase de configuration était très fastidieuse. Par exemple, un plugin Cocoapods était récemment sorti pour simplifier l’utilisation de la bibliothèque générée sur Xcode alors qu’ il était nécessaire de le faire à la main auparavant.

Conclusion

Kotlin/Native est finalement un puissant argument à intégrer dans le large débat de la mutualisation et se démarque des technologies existantes par :

  • Sa facilité d’intégration dans un projet du fait qu'il sert à créer un ensemble de fonctionnalités rassemblées dans un package natif réutilisable plutôt que de créer toute une application hybride.
  • Sa facilité pour appeler du code natif qui est une réponse aux défauts d’autres technologies telles que PWA ou React Native.
  • L’utilisation de Kotlin qui est aussi utilisé par les développeurs Android et nous évite de réapprendre une nouveau langage.

Malgré les quelques complications encourues durant mon expérimentation, ce framework a évolué en très peu de temps et aujourd'hui de plus en plus de collaborations se font autour de cette technologie telles que celle de Square et Touchlab. Pour ceux qui souhaitent voir plus de concret, je vous invite à regarder le fruit de mes expérimentations disponible sur Github.