Une histoire impressionante

Peu de choses ont été écrites sur le portage d’applications volumineuses sur Flutter. Quand je l’ai fait, j’ai été surpris des résultats.

Gary Hunter

 

C’est une vérité universellement reconnue, selon laquelle une entreprise possédant une bonne application mobile doit avoir besoin d’une version iOS et Android.

La logique métier, une seule implémentation, enveloppé dans un rapide,
l’interface utilisateur native est ce que la plupart des entreprises souhaitent.

Mais à quel point est-ce difficile ?

Jusqu’à l’année dernière, mon entreprise ne disposait que d’une version native iOS de notre application principale, Easy Diet Diary. Un traqueur de régime australien à usage général avec des versions spéciales pour la recherche et pour les personnes assez malchanceuses pour avoir une maladie rénale. L’application a:

  • 75 000 lignes de code Objective C et Swift conformes au cloc
  • un backend Amazon AWS: DynamoDB, Postgres et S3
  • 22 000 utilisateurs quotidiens et 1,25 million de téléchargements

Puis Flutter est arrivé (version bêta 2 avril 2018)

Nous avons pu cocher suffisamment de cases (multiplate-forme, bonnes performances, mise en œuvre rapide, sensation native, open-source) pour pouvoir créer une seule version de Flutter pour iOS et Android.

Après six mois, j’ai publié une bêta ouverte de Google sans recourir au code natif. J’ai écrit sur l’expérience ici.
Cet article concerne le passage de la bêta ouverte à :

 

  • La version Publié Android dans le Play Store de Google.
  • Remplacement instantané de l’application iOS native d’origine dans l’App Store.
application mobile devellopeur casablanca
Apparemment, les gens apprécient d’avantage les articles techniques avec des photos aléatoires d’animaux mignons.
Alors, J’ai choisi pour vous une image d’un chat prise à Marrakech, preseverant, et avec plein d’espoir. credit : TARIK JARI

Lignes de code et vitesse de développement

Lorsque j’ai démarré le portage, je me suis rendu compte à quel point j’étais productif avec la programmation déclarative et comment je pouvais réutiliser le code d’interface au lieu de l’inclure de manière inaccessible dans des storyboards basés sur XML. Eh bien, avec l’introduction de Jetpack Compose et de SwiftUI, il semble qu’il n’y ait plus rien à voir ici.

J’ai fini avec 35 000 lignes de code Dart. En outre, 3000 lignes de code Objective-C / Swift permettent de gérer des éléments spécifiques à iOS, tels que HealthKit et la mise à niveau d’utilisateurs hérités, ainsi que 500 lignes de code de traitement d’images Java.

Après le portage, l’application Flutter contenait la moitié autant de lignes de code que l’application iOS originale.

Google Open Beta

J’ai passé beaucoup de temps à manipuler des applications via le processus Test-Flight d’Apple et il est difficile de mettre une application en évolution à la portée du public. Et je ne m’attends pas à ce que cela change de si tôt car Apple revendique son processus de révision comme un moyen de s’assurer que les applications respectent certaines normes et ne sont pas malveillantes. Pour un développeur compétent avec de bonnes intentions, cela peut être frustrant.

En revanche, à l’aide du processus de bêta ouverte de Google, le public peut rechercher des applications bêta dans le Play Store, comme toute autre application, et rejoindre assez facilement le programme bêta pour utiliser l’application et donner des commentaires (limités). Lorsque vous êtes satisfait d’une version bêta ouverte, vous pouvez la promouvoir comme version générale. Si une application est raisonnablement utilisable, les utilisateurs le comprennent bien et fournissent un retour d’information constructif. Au moins cela a été mon expérience. C’est un excellent moyen de se développer.

Easy Diet Diary a accumulé 10 000 utilisateurs bêta alors que j’ajoutais des fonctionnalités et corrigeais des bugs. J’ai publié une version 1.0 d’Android en mars.

Architecture :

Lorsque j’ai commencé l’année dernière, j’étais novice dans la programmation d’interface utilisateur déclarative et dans le type de gestion d’état associée. Pour moi, Redux et BLoC, avec leur dépendance à l’égard des flux asynchrones, avaient une courbe d’apprentissage abrupte et j’ai fini par utiliser Inherited-Widgets pour synchroniser l’état dans l’arborescence des widgets. J’ai fait de mon mieux pour séparer la logique métier de la logique de présentation, mais je n’ai pas utilisé de cadre de gestion d’états pour imposer la séparation.

Depuis lors, les approches et discussions animées sur la gestion de l’État se sont multipliées. Il est intéressant de mettre en contraste l’évolution de la gestion des états en open source dans Flutter avec le développement du cadre de programmation réactive de SwiftUI – une grande équipe travaillant en secret. Très Steve Jobs. Matt Gallagher a une analyse intéressante du framework Combine d’Apple.

Chez Google I/O 2019, l’équipe Flutter cherche à rendre la gestion des états moins pénible pour les nouveaux développeurs et pour réduire la prolifération des encapsuleurs InheritedWidget, a promu le widget fournisseur de Remi Rousselet décrit ici. J’ai remplacé mes modestes efforts par un provider. Je n’utilise toujours pas d’approche de gestion d’état plus formelle, telle que BLoC ou MobX (les deux peuvent être utilisées parallèlement à provider), car cela nécessiterait des modifications substantielles du code.

Didier Boelens m’a aidé à me familiariser avec la gestion de l’Etat. Il se trouve qu’il est un fan de BLoC mais qu’il sait bien expliquer les choses de manière impartiale.

Services principaux (Amazon AWS)

Mis à part Crashlytics et ML Kit, tous les services cloud d’Easy Diet Diary sont sur Amazon AWS.

Malheureusement, jusqu’à présent, il n’y a pas de SDK Flutter officiel pour AWS et très peu de plug-ins liés à AWS.

Pour moi, cela n’a pas été un facteur décisif, car nous avons notre propre serveur situé entre notre application mobile et tous nos services AWS… à l’exception de S3 (stockage de fichiers dans le cloud). Notre serveur prend en charge l’authentification et la synchronisation des journaux des utilisateurs stockés dans DynamoDB.

En contournant notre serveur, l’application iOS native a utilisé le kit SDK AWS S3 pour télécharger directement des photos. Pour passer à Flutter, je devais passer aux URL S3 pré-signées (fournies par AWS via notre serveur). Ça marche bien.

Bien que je n’ai pas trop souffert lors du transfert de données vers Flutter, je pense que Flutter serait beaucoup plus populaire avec les plugins AWS SDK (ils ne doivent pas nécessairement être officiels). Si les revenus du cloud sont un indicateur approximatif de l’utilisation du cloud dans les applications mobiles, AWS est de loin le leader du marché.

J’aimerais pouvoir utiliser Dart pour expérimenter le nombre croissant de services AWS. Je suis sûr que la même chose pourrait être dite pour Azure, bien que je ne l’aie pas utilisée.

Performance

Les choses qui testent les performances de Flutter dans Easy Diet Diary sont, par exemple, l’animation d’une fenêtre de visualisation de la caméra à mi-hauteur de l’écran (au chargement de la caméra) ou l’affichage de graphiques après la lecture de centaines de fichiers JSON de 20 Ko.

En comparant les versions natives iOS et Flutter de l’application, nos testeurs (c’est en fait principalement un testeur) n’ont pas subi de dégradation notable des performances de nos téléphones de test, sauf sur un iPhone 6 (l’application fonctionnait bien sur un 6S). Le 6 est sorti en 2014 mais il en reste quelques-uns. Mon fils adolescent en a un. Parmi nos autres téléphones test, l’un des plus lents est le Samsung Galaxy J5 Pro et le Motorola G5S Plus (mon téléphone en cours de portage).

Lorsque j’ai démarré, l’iPhone 6 affichait des mouvements saccadés et saccadés, en particulier lors de l’animation de la fenêtre de l’appareil photo, mais diminuait (mais ne disparaissait pas complètement) au cours des mois qui ont suivi la sortie des nouvelles versions de Flutter. L’application fonctionnait bien sur les autres téléphones de test plus lents.

Sur une note distincte, mon téléphone personnel est aujourd’hui un Pixel 3a, un téléphone de milieu de gamme sous Android 10. Il fonctionne à merveille avec Easy Diet Diary (bien sûr). Téléphones Android que j’ai utilisés. Cela convient parfaitement à mon état d’esprit multi-plateforme en évolution. Tout ce dont il a besoin, ce sont des listes gonflables.

Code natif

J’ai essayé de l’éviter.

J’ai été déçu par le traitement des images, l’intégration de HealthKit et la mise à niveau des utilisateurs traditionnels.

Pour HealthKit et la mise à niveau d’anciens utilisateurs, j’ai simplement utilisé le code Swift et Objective C de la version iOS native.

Pour le traitement des images, j’ai écrit mon premier code Java. Les utilisateurs de l’application prennent des photos dans diverses situations. Les photos de plusieurs mégapixels prises par les téléphones modernes prennent beaucoup de place, aussi je recadre, redimensionne et compresse avant l’affichage ou le téléchargement. Ce traitement nécessite du code natif iOS et Android. Je ne trouvais pas de plug-in qui fonctionnait pour moi (conserver correctement les métadonnées EXIF ​​était un problème particulier). J’ai donc construit ma propre solution à partir de plug-ins open source proches de ce que je cherchais, des réponses StackOverflow et de mon ancien code Objective-C. .

Les packages de plugins sont faciles à écrire si vous suivez les instructions à la lettre. Une fois que vous êtes opérationnel, vous pouvez configurer votre application pour qu’elle se lance à partir de votre code natif et définisse des points d’arrêt, etc. Bien entendu, il n’y a pas de rechargement à chaud.

J’avais espéré décharger le traitement sur un isolat qui pourrait ensuite prendre son temps pour le traitement et le téléchargement des images. Malheureusement, vous ne pouvez pas appeler le code du plugin à partir d’un isolat. Au lieu de cela, j’ai créé un fil dans le plugin natif.

Conception multiplateforme

Flutter vous permet de contrôler à quel point vous souhaitez rendre votre application native. Comme je rédigeais une solution de remplacement pour une application iOS native avec des utilisateurs existants, je souhaitais que l’application Flutter soit très similaire à l’originale.

J’y suis arrivé en construisant d’abord une application de conception de matériaux. Pour me plonger dans le processus, j’ai remplacé mon téléphone personnel d’un iPhone par un appareil Android de milieu de gamme (un Motorola G5S Plus). Je ne voulais pas quelque chose de trop haut de gamme.

Puis, après avoir géré l’application via le processus Open Beta, je lui ai donné une métamorphose sur iOS. Cependant, je ne choisirais pas cet itinéraire aujourd’hui. Quand j’ai commencé, il y avait un nombre limité de widgets de style iOS. Depuis lors, la liste a augmenté au point où vous pouvez créer simultanément une application Material Design et de style iOS et changer de widgets dans votre arborescence de widgets, le cas échéant.

Je n’entrerai pas dans les détails des modifications multi-plateformes que j’ai apportées. C’est une histoire en soi. Je dirai que:

certaines des grandes différences étaient assez faciles à mettre en œuvre. Par exemple, dans la mesure où l’interface utilisateur contient tout le code de l’arborescence des widgets, le remplacement d’un menu de tiroir coulissant sous Android par un bouton Plus dans la barre d’onglets inférieure dans iOS est agréablement simple.
certaines des petites différences étaient assez délicates. Par exemple, l’utilisation du même code pour afficher une boîte de dialogue d’apparence native sur les deux plates-formes a été assez nuancée. Peut-être que ce n’est pas une petite différence?

Après avoir terminé, j’ai trouvé ce document: Comportements et adaptations spécifiques à la plate-forme. J’aurais aimé l’avoir quand j’ai commencé !

Saveurs et Schémas

Flutter est conçu pour créer des applications multiplates-formes à partir de la même base de code, mais qu’en est-il de la création de plusieurs applications à partir de la même base de code sur une seule plate-forme?

Pour moi, cela a pris plus de temps que prévu, mais au final, j’ai appris à mieux faire les choses.

Cela semble simple. Flutter dispose d’un commutateur de ligne de commande (également configurable via l’EDI) qui vous permet de spécifier un type de construction mappé sur un modèle de produit Gradle ou un modèle Xcode.

flutter build –flavor research

J’espérais reproduire ce qui avait été configuré dans le projet Xcode natif. Dans Xcode, vous différenciez les versions d’applications à l’aide de noms de schéma. Les régimes sont simples. Lorsque vous créez un schéma, vous indiquez simplement à Xcode la configuration et la cible que vous souhaitez utiliser:

Une configuration mappe sur un fichier texte ordinaire avec une extension .xcconfig. Vous y spécifiez les variables d’environnement propres à la version de l’application que vous créez. Un suffixe d’identifiant de paquet par exemple.
Une cible est constituée de tous vos paramètres de construction ( il en existe des centaines ) ainsi que d’une liste des classes, ressources et scripts personnalisés à inclure dans la construction. Un nouveau projet Xcode a une cible. Dans Flutter, cela s’appelle « Runner » .

Dans l’application iOS native, il y avait différentes cibles pour différentes versions de l’application. C’est comme ça que la plupart des gens le font. Faire fonctionner Flutter avec différentes cibles s’est toutefois révélé insaisissable (c’est-à-dire que cela a presque fonctionné).

À la fin, je suis arrivé à la même conclusion que Salvatore Giordano, n’utilisant qu’une cible unique et reposant entièrement sur des configurations. Ensuite, j’ai lu cet excellent article de Mattt Thompson de NSHipster et d’Alamofire et je me suis détendu. Selon Mattt, les fichiers de configuration avec une seule cible sont la voie à suivre avec les avantages suivants:

Ils peuvent être gérés sans Xcode. Ils sont en texte brut, ce qui signifie qu’ils sont beaucoup plus conviviaux pour les systèmes de contrôle de source et qu’ils peuvent être modifiés avec n’importe quel éditeur.

Vous pouvez les utiliser pour définir l’identifiant de l’ensemble, le jeu d’icônes de l’application, le fichier d’informations de service Google à utiliser (explique Vasily Bodnarchuk), etc.

De plus, le fait de ne pas pouvoir utiliser différentes cibles dans iOS m’a obligé à comprendre exactement ce qui différenciait les versions de l’application iOS et qui facilitait la configuration des versions de produits Android.

Conclusion :

La façon dont Flutter est mis en place :

  • sa plate-forme sous-jacente qui ressemble un peu au moteur de jeu en métal.
  • son arborescence de widgets avec  »hot reload » qui vous permet de construire des interfaces utilisateur rapidement et de manière flexible.
  • sa gamme complète de widgets de style natif pour Android et iOS pouvant être facilement basculés dans l’arborescence des widgets en fonction de la plate-forme.
  • sa base de code open-source qui vous permet de déboguer des problèmes et de modifier ou d’améliorer les widgets selon les besoins ou simplement voir comment ça marche.

    C’est vraiment bon.