Theodo apps

Migration d’une application iOS/Android vers Kotlin Multiplatform : Méthodologie et étapes clés

L’intérêt principal de cette migration ? Migrer du natif vers le multiplateforme, c’est avant tout réduire les coûts et le lead time pour livrer de nouvelles fonctionnalités. Maintenant, pourquoi choisir une migration vers KMP plutôt que Flutter ou React Native ? Car elle peut être réalisée par étapes, en migrant une couche applicative ou une fonctionnalité à la fois, limitant ainsi les risques et facilitant l’intégration.

J’ai moi-même participé à ce genre de migration. Dans ce premier article, je vous explique la méthodologie suivie.

🚀 Dans un deuxième article, je partagerai un retour d’expérience basé sur l’application concrète de cette méthodologie.

🔍 0. Identifier les différences entre les plateformes

Avant de débuter la migration, il est crucial d’identifier les écarts entre les versions iOS et Android de votre application. La migration KMP offre l’opportunité d’unifier certaines de ces différences de comportement, si cela s’avère pertinent.

  1. Lister toutes les différences fonctionnelles entre les deux applications.
  2. Déterminer si elles sont intentionnelles ou non.
  3. Pour chaque différence non souhaitée, définir une version unifiée à adopter dans l’architecture KMP.

➡️ Pourquoi cette étape est essentielle ?

Elle évite la création d’une implémentation KMP trop spécifique à Android et garantit que les avantages de la version iOS ne soient pas perdus.

Il existe aussi des différences d’implémentation, ce qui est normal puisque iOS et Android ont chacun leurs spécificités. Pour les différences d’architecture, par contre, notez-les : cela vous aidera à estimer la difficulté de la migration progressive par couche. Plus les couches d’architecture sont différentes, plus la migration par couche sera complexe côté iOS.

🛠 1. Préparer le projet Android à la migration

Avant de commencer, il est impératif d’effectuer certaines mises à jour :

  • Assurez-vous d’avoir des versions récentes de Kotlin, KSP et Gradle pour bénéficier des dernières optimisations et éviter les incompatibilités.
  • Certaines bibliothèques tierces utilisées sur Android (Koin, Room, Coil, etc.) sont utilisables sur KMP. Mettez-les à jour avant de démarrer.
  • Jetpack Compose évolue rapidement, une mise à jour permet d’exploiter les dernières améliorations (si migration de la vue).

➡️ L’objectif est d’assurer une transition fluide sans surprises techniques bloquantes.

🔄 2. Créer le projet multiplateforme

Plusieurs solutions s’offrent à vous ; elles sont détaillées sur le site de KMP.

Je vous conseille la solution suivante, plus simple pour la maintenance du projet migré :

  • Transformez l’un des répertoires Git (Android/iOS) en un répertoire commun contenant les deux applications Android et iOS ainsi que le futur code partagé. Cette solution nécessite une manipulation pour conserver l’historique Git des deux projets (par exemple avec des sous-modules Git).

Cette solution a ses limitations. Pour aller plus loin, vous pouvez consulter cet article de TouchLab (un leader sur KMP) qui parle de leur outil Gitportal.

👁️ 3. Trouver la fonctionnalité la plus facile à migrer

La migration doit se faire par petites étapes. Commencez par une fonctionnalité isolée, non centrale, une fonctionnalité feuille ou périphérique, pour minimiser les risques.

Comment trouver une fonctionnalité feuille ?

  • Si votre application est constituée de flux, regardez du côté des fins de flux ou des flux secondaires.
  • Si votre application comporte une barre de navigation en bas, choisissez l’onglet le moins lié aux fonctionnalités principales.

📦 4. Migrer la couche modèle d’une fonctionnalité

🔹 Pourquoi commencer par la couche modèle ?

C’est la couche qui contient normalement le moins de dépendances à des librairies externes. Et donc le moins de code spécifique à une platforme.

🟦 Comment attaquer la couche modèle ?

  1. Convertir autant de code que possible en utilisant des API Kotlin pures plutôt que des API spécifiques à Java ou Android (par exemple, remplacer java.util.UUID par kotlin.uuid.Uuid).
    • Ceci s’applique à la migration de tous les modules.
    • C’est ici tout le cœur de la migration Android → KMP : vous remplacez du code spécifique à la JVM et au SDK Android par du code compatible KMP, compilable sur toutes les plateformes.
  2. Dans le module commun multiplateforme, créez le module model de votre fonctionnalité en dupliquant les classes de votre code Android tout en tenant compte des éventuelles différences avec iOS.La couche modèle est la plus simple à migrer, car elle n’a pas (ou peu) de dépendances externes. C’est le cœur de votre application et la couche qui présente généralement le moins de différences entre les plateformes.
    • Utilisez votre module multiplateforme pour déplacer les sous-parties quand c’est possible, en faisant des commits de taille correcte.
  3. Dans votre code Android et iOS, remplacez les imports des anciennes classes model par ceux des classes du module commun.
    • Si vous utilisez le même package dans le code KMP que sur Android, vous éviterez cette étape et limiterez au maximum les retouches.

🔄 5. Migrer les modules transverses

Après avoir migré la couche modèle avec succès, on pourrait être tenté de passer sur les couches suivantes. Cependant, les couches suivantes auront probablement besoin de vos modules transverses. Par exemple, la couche data de votre fonctionnalité a besoin du module qui crée le client HTTP. Vous aurez aussi besoin de votre logger, des analytics, des fonctions utils et même de votre design system à un moment.

Il serait donc intéressant de migrer ces modules transverses d'abord. Toutefois, il peut être difficile de se rendre compte si l'on a besoin de migrer tout un module ou seulement une partie liée à la feature que l'on migre.

Il est donc recommandé de continuer les étapes de migrations suivantes en revenant aux modules transverses dès que nécessaire.

🔗 6. Migrer la couche Data de la même fonctionnalité

La couche Data gère les appels réseau, les accès aux bases de données et les manipulations de fichiers. Elle est étroitement liée à la logique métier. Migrez chaque partie de cette couche une par une.

  1. Choisissez une bibliothèque KMP pour les appels réseau et la base de données.
  2. Migrez vos classes de la couche Data en utilisant les bibliothèques KMP.
  3. Créez des adaptateurs pour convertir les données réactives communes en données réactives spécifiques à Android ou iOS. En effet, votre couche de logique métier va demander des données réactives à votre couche data. C'est donc votre couche métier qui va utiliser ces adaptateurs.

Vous aurez plus de facilité si vous utilisez déjà les coroutines ou Kotlin Flow plutot que Rx et les Livedata.

Dans le cas où vos données interagissent avec celles d’autres fonctionnalités, trois choix s'offrent à vous :

  • Créer des composants spécifiques pour assurer les interactions avec les données dans toutes les fonctionnalités.
  • Revoir le choix de la fonctionnalité migrée en premier et opter pour une fonctionnalité avec moins de dépendances.
  • Migrer les données des autres fonctionnalités en même temps si cela reste gérable.

🏗 7. Migrer la couche métier de la même fonctionnalité

La partie logique métier contient les règles spécifiques à votre application. Cette couche, proche de la couche modèle, inclut souvent des fonctions qui récupèrent, mettent à jour ou suppriment des données. Elle peut être fortement liée à des bibliothèques externes comme Rx ou LiveData pour rendre les données réactives.Elle communique avec la couche Data pour récupérer les données et les transformer.

Pour migrer cette couche, déplacez vos adaptateurs de code réactif pour les utiliser dans la couche présentation plutôt que dans la couche métier. En faisant cela, vous allez conformer votre couche métier aux librairies de réactivités Kotlin (coroutines, flow).

🖼 8. Migrer la couche présentation de la même fonctionnalité

Cette couche, qui fait appel à la logique métier pour nourrir les vues, est souvent la moins bien architecturée. En effet, le cœur d’une application mobile étant d’organiser l’affichage des données de réagir aux interactions de l’utilisateur, c’est par ici que passe la plupart du code. Par ailleurs c’est la couche qui communique avec les vues, on y retrouve donc souvent du code très (trop ?) proche de la plateforme.Soyez très attentif, c'est ici que vous risquez le plus d'avoir des régressions, complétez vos TUs au besoin.

🎯 Stratégie recommandée

  • Identifier les parties trop liées aux frameworks et les adapter.
  • Vérifier chaque modification pour éviter les régressions.
  • Supprimer les adaptateurs de l’étape 5, s’ils ne sont plus nécessaires.

🎨 9. Migrer la vue de la même fonctionnalité

Si vous souhaitez partager la vue entre les plateformes avec Compose Multiplatform, c’est le moment de le faire. Dupliquez les vues Android en réimplémentant les éléments spécifiques à Compose pour Android. Compose Multiplatform étant très proche de Compose pour Android, les modifications nécessaires seront limitées. Une fois que tous les éléments spécifiques à Android seront écartés, votre code Compose sera utilisable sur iOS.

En migrant également les vues, vous limiterez considérablement la communication entre le code KMP et le code Swift. Bien que la promesse de KMP soit la facilité de communication entre le code commun et spécifique, il est toujours plus simple de ne pas avoir à le faire.

✅ 10. Finaliser la migration

À cette étape, la plupart du travail est fait. Il ne reste que quelques éléments spécifiques aux plateformes comme la navigation ou certaines intégrations natives.. Finalisez ces migrations progressivement, sans précipitation.

🤔 Faut-il migrer progressivement iOS ?

Dans cette stratégie progressive, le code commun est conçu pour s’adapter à iOS, ce qui permet de ne pas bloquer le développement de l’application iOS tout en s’assurant que l’API du code commun est bien adaptée à cette plateforme.

Plus le code des plateformes est différent dans son architecture, ses composants, ses noms, ses comportements et sa logique, moins la migration progressive pour iOS est intéressante. Cette stratégie peut devenir chronophage et une approche moins progressive pourrait être plus adaptée. On peut se limiter par exemple à une migration progressive feature par feature.

Conclusion

Cette première partie détaille une méthodologie structurée pour migrer progressivement une application mobile vers Kotlin Multiplatform. Plus votre code Android sera loin de l’univers Android et Java et proche de Kotlin, plus la migration sera facile. Plus votre architecture et les interfaces entre les couches seront identiques sur Android et iOS, plus vous aurez l’avantage de migration progressive par couche sur iOS.

Dans la deuxième partie, nous partagerons un retour d’expérience sur l’application concrète de ces étapes, avec les défis rencontrés et les enseignements tirés.

📌 Restez connectés pour la suite ! 🚀

Développeur mobile ?

Rejoins nos équipes