<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Kittysplit Blog</title>
    <description></description>
    <link>https://blog.kittysplit.com/fr/</link>
    <language>fr</language>
    <atom:link href="https://blog.kittysplit.com/fr/feed.xml" rel="self" type="application/rss+xml" />
    
      <item>
        <title>Voici les comptes&amp;nbsp;: tes Kitties, en sécurité et synchronisées</title>
        <description>&lt;p&gt;Les comptes sont arrivés. Connecte-toi sur Kittysplit (web, iOS ou Android) et toutes tes Kitties te suivent. C’est notre plus grosse mise à jour depuis un bon moment, et elle est entièrement facultative.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2026-04-23/accounts-title.fr.png&quot; alt=&quot;Comptes Kittysplit&quot; title=&quot;Comptes Kittysplit&quot; /&gt;&lt;/p&gt;

&lt;!-- read-more --&gt;

&lt;h2 id=&quot;pourquoi-des-comptes&quot;&gt;Pourquoi des comptes ?&lt;/h2&gt;

&lt;p&gt;Pendant longtemps, Kittysplit a fonctionné sans aucun compte, et c’était voulu. Pas de compte, ça veut dire pas d’inscription, pas de mot de passe à retenir et aucune donnée personnelle collectée. À l’époque, on &lt;a href=&quot;/webapp-vs-native-app/&quot;&gt;n’avait pas non plus d’applis mobiles&lt;/a&gt;, donc changer d’appareil posait beaucoup moins de problèmes.&lt;/p&gt;

&lt;p&gt;Mais les comptes étaient la demande n° 1 de notre sondage, et on l’entend souvent depuis le &lt;a href=&quot;/fr/application-mobile-kittysplit/&quot;&gt;lancement des applis mobiles&lt;/a&gt; l’an dernier. Alors voilà, les comptes sont enfin là ! Créer des comptes, ce n’est pas si compliqué. Les garder facultatifs, c’est là que ça devient intéressant. Il a fallu pas mal de réflexion pour qu’une Kitty fonctionne exactement pareil, que tu sois connecté ou non.&lt;/p&gt;

&lt;h2 id=&quot;tes-kitties-sur-tous-tes-appareils&quot;&gt;Tes Kitties sur tous tes appareils&lt;/h2&gt;

&lt;p&gt;Connecte-toi sur le web ou sur ton téléphone et tu retrouveras les mêmes Kitties partout. Fini de fouiller dans de vieilles conversations pour retrouver le lien. Nouveau téléphone ? Autre navigateur ? Connecte-toi et reprends là où tu t’étais arrêté.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;&lt;img src=&quot;/images/2026-04-23/device_screen_shot_1.png&quot; alt=&quot;Se connecter ou s&apos;inscrire&quot; title=&quot;Se connecter ou s&apos;inscrire&quot; width=&quot;320&quot; /&gt;&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;&lt;img src=&quot;/images/2026-04-23/device_screen_shot_2.png&quot; alt=&quot;Ton profil&quot; title=&quot;Ton profil&quot; width=&quot;320&quot; /&gt;&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;em&gt;Se connecter ou s’inscrire&lt;/em&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;em&gt;Ton profil&lt;/em&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2 id=&quot;des-kitties-plus-sûres&quot;&gt;Des Kitties plus sûres&lt;/h2&gt;

&lt;p&gt;Si tu crées une Kitty en étant connecté, elle est à toi. Il n’y a que toi qui peux la modifier ou la supprimer, donc rien d’important ne disparaît par erreur. Tes amis peuvent toujours ajouter des dépenses et régler leurs comptes comme avant. Pour eux, rien ne change.&lt;/p&gt;

&lt;h2 id=&quot;tes-amis-nont-pas-besoin-de-compte&quot;&gt;Tes amis n’ont pas besoin de compte&lt;/h2&gt;

&lt;p&gt;Les comptes sont facultatifs, et ils le resteront. Invite tes amis comme d’habitude : envoie-leur le lien et ils peuvent commencer à partager les dépenses tout de suite. Pas d’inscription, pas de prise de tête. Ça reste le cœur de Kittysplit.&lt;/p&gt;

&lt;h2 id=&quot;bientôt-les-photos-de-profil&quot;&gt;Bientôt : les photos de profil&lt;/h2&gt;

&lt;p&gt;Prochaine étape, les photos de profil : ajoute une jolie photo pour être un peu plus qu’un simple nom dans les Kitties de tes amis. Et on a encore quelques idées en réserve, rien que pour les comptes.&lt;/p&gt;

&lt;p&gt;Bon partage ! 🐱✨&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;Un retour ou une idée ? Écris-nous sur &lt;a href=&quot;https://x.com/kittysplit&quot;&gt;X&lt;/a&gt;, &lt;a href=&quot;https://bsky.app/profile/kittysplit.bsky.social&quot;&gt;Bluesky&lt;/a&gt;, &lt;a href=&quot;https://www.instagram.com/kittysplit.app/&quot;&gt;Instagram&lt;/a&gt; ou par e-mail à info[at]kittysplit.com&lt;/em&gt;&lt;/p&gt;
</description>
        <pubDate>Thu, 23 Apr 2026 00:00:00 +0000</pubDate>
        <link>https://blog.kittysplit.com/fr/comptes-kittysplit/</link>
        <guid isPermaLink="true">https://blog.kittysplit.com/fr/comptes-kittysplit/</guid>
      </item>
    
      <item>
        <title>Pièces jointes&amp;nbsp;: ajoute des photos à tes dépenses</title>
        <description>&lt;p&gt;On a ajouté les pièces jointes aux dépenses dans Kittysplit ! Tu peux maintenant joindre jusqu’à 3 images à n’importe quelle dépense de ta Kitty. C’est une fonctionnalité &lt;strong&gt;Super Kitty&lt;/strong&gt;, il faudra donc mettre ta Kitty à niveau pour en profiter.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2026-03-12/expense-attachments-1.png&quot; alt=&quot;Pièces jointes&quot; title=&quot;Pièces jointes&quot; width=&quot;320&quot; /&gt;&lt;/p&gt;

&lt;!-- read-more --&gt;

&lt;h2 id=&quot;quoi-de-neuf&quot;&gt;Quoi de neuf ?&lt;/h2&gt;

&lt;p&gt;Chaque dépense peut maintenant avoir ses photos. Prends en photo le ticket de caisse, l’addition du resto ou ce que vous partagez, et la photo reste là, avec la dépense, sous les yeux de tout le monde.&lt;/p&gt;

&lt;p&gt;Tu peux ajouter jusqu’à 3 images par dépense, ce qui devrait suffire dans la plupart des cas. Tickets de caisse, factures d’hôtel, billets de concert, ou juste une photo de ce que tu as acheté, pour que le groupe sache à quoi il participe.&lt;/p&gt;

&lt;p&gt;Fini de faire défiler ta galerie des semaines plus tard pour retrouver ce fameux ticket. Tu le joins en ajoutant la dépense, et c’est réglé.&lt;/p&gt;

&lt;h2 id=&quot;comment-ça-marche&quot;&gt;Comment ça marche&lt;/h2&gt;

&lt;p&gt;Quand tu ajoutes ou modifies une dépense, tu vois une option pour joindre des images. Choisis-en une dans ta galerie. Si tu veux la remplacer plus tard, supprime-la et ajoutes-en une nouvelle.&lt;/p&gt;

&lt;h2 id=&quot;à-toi-de-jouer&quot;&gt;À toi de jouer&lt;/h2&gt;

&lt;p&gt;Passe à la Super Kitty et joins des photos à ta prochaine dépense. C’est tout !&lt;/p&gt;

&lt;p&gt;Bon partage ! 🐱✨&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;Un retour ou une idée ? Écris-nous sur &lt;a href=&quot;https://x.com/kittysplit&quot;&gt;X&lt;/a&gt;, &lt;a href=&quot;https://bsky.app/profile/kittysplit.bsky.social&quot;&gt;Bluesky&lt;/a&gt;, &lt;a href=&quot;https://www.instagram.com/kittysplit.app/&quot;&gt;Instagram&lt;/a&gt; ou par e-mail à info[at]kittysplit.com&lt;/em&gt;&lt;/p&gt;
</description>
        <pubDate>Thu, 12 Mar 2026 00:00:00 +0000</pubDate>
        <link>https://blog.kittysplit.com/fr/pieces-jointes-pour-les-depenses/</link>
        <guid isPermaLink="true">https://blog.kittysplit.com/fr/pieces-jointes-pour-les-depenses/</guid>
      </item>
    
      <item>
        <title>Voici les catégories de dépenses&amp;nbsp;: y voir plus clair dans tes Kitties</title>
        <description>&lt;p&gt;On est ravis de te présenter une nouveauté bien pratique dans Kittysplit : les &lt;strong&gt;catégories de dépenses&lt;/strong&gt; ! À partir d’aujourd’hui, tu peux classer chaque dépense et chaque recette de tes Kitties par catégorie, pour mieux voir où part ton argent.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;&lt;img src=&quot;/images/2026-02-11/expense-categories-1.png&quot; alt=&quot;Ajouter une dépense&quot; title=&quot;Ajouter une dépense&quot; width=&quot;320&quot; /&gt;&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;&lt;img src=&quot;/images/2026-02-11/expense-categories-2.png&quot; alt=&quot;Filtrer par catégorie&quot; title=&quot;Filtrer par catégorie&quot; width=&quot;320&quot; /&gt;&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;em&gt;Ajouter une dépense&lt;/em&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;em&gt;Filtrer par catégorie&lt;/em&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;!-- read-more --&gt;

&lt;h2 id=&quot;quoi-de-neuf&quot;&gt;Quoi de neuf ?&lt;/h2&gt;

&lt;p&gt;Chaque dépense ou recette que tu ajoutes à ta Kitty peut maintenant être rangée dans l’une de ces 12 catégories :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Logement&lt;/strong&gt; : loyer, crédit immobilier, assurance habitation et indispensables de la maison&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Hébergement&lt;/strong&gt; : hôtels, locations de vacances et séjours temporaires&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Courses et alimentation&lt;/strong&gt; : passages au supermarché et provisions pour la maison&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Restaurants et sorties&lt;/strong&gt; : restaurants, bars, cafés et plats à emporter&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Transport&lt;/strong&gt; : carburant, transports en commun, VTC et location de voiture&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Charges et communications&lt;/strong&gt; : électricité, eau, internet et téléphone&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Santé&lt;/strong&gt; : frais médicaux, pharmacie et mutuelle&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Loisirs et activités&lt;/strong&gt; : divertissements, activités, concerts et événements&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Shopping et équipement&lt;/strong&gt; : vêtements, électronique et achats en tout genre&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Bien-être et soins&lt;/strong&gt; : salle de sport, spa, beauté et soins perso&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Finances et frais&lt;/strong&gt; : frais bancaires, frais de service et autres coûts financiers&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Divers&lt;/strong&gt; : tout ce qui ne rentre nulle part ailleurs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Chaque catégorie a sa propre couleur, ce qui permet de repérer les tendances d’un coup d’œil.&lt;/p&gt;

&lt;h2 id=&quot;filtre-tes-dépenses&quot;&gt;Filtre tes dépenses&lt;/h2&gt;

&lt;p&gt;La première fonctionnalité qu’on lance avec les catégories, c’est le &lt;strong&gt;filtrage des dépenses&lt;/strong&gt;. Tu peux maintenant filtrer ta liste de dépenses par catégorie pour retrouver vite :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;toutes les dépenses &lt;strong&gt;restaurants&lt;/strong&gt; de votre voyage entre amis&lt;/li&gt;
  &lt;li&gt;les &lt;strong&gt;courses&lt;/strong&gt; de votre coloc&lt;/li&gt;
  &lt;li&gt;les frais de &lt;strong&gt;transport&lt;/strong&gt; de votre road trip&lt;/li&gt;
  &lt;li&gt;n’importe quelle catégorie que tu veux passer en revue&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Du coup, c’est super facile de répondre à des questions comme « Combien on a dépensé en nourriture ? » ou « Combien nous a coûté l’hébergement pendant les vacances ? »&lt;/p&gt;

&lt;h2 id=&quot;et-ensuite&quot;&gt;Et ensuite ?&lt;/h2&gt;

&lt;p&gt;Ce n’est qu’un début. On travaille déjà sur les prochaines fonctionnalités, pour que tu tires encore plus de tes dépenses classées par catégorie.&lt;/p&gt;

&lt;h3 id=&quot;comparaisons-dun-mois-à-lautre&quot;&gt;Comparaisons d’un mois à l’autre&lt;/h3&gt;
&lt;p&gt;Suis l’évolution de tes dépenses dans chaque catégorie. Regarde si la note des courses grimpe doucement, ou si tu dépenses moins au resto que le mois dernier.&lt;/p&gt;

&lt;h3 id=&quot;analyse-des-tendances&quot;&gt;Analyse des tendances&lt;/h3&gt;
&lt;p&gt;Repère tes habitudes de dépenses sur plusieurs semaines, plusieurs mois, voire plusieurs années. Idéal pour les colocs qui veulent mieux gérer leur budget commun.&lt;/p&gt;

&lt;h3 id=&quot;rapports-visuels&quot;&gt;Rapports visuels&lt;/h3&gt;
&lt;p&gt;Des graphiques pour voir la répartition de tes dépenses par catégorie : des camemberts pour la vue d’ensemble, des histogrammes pour comparer les catégories et une vue chronologique pour suivre l’évolution.&lt;/p&gt;

&lt;h2 id=&quot;pourquoi-les-catégories-cest-utile&quot;&gt;Pourquoi les catégories, c’est utile&lt;/h2&gt;

&lt;p&gt;Que tu partages un appart avec des colocs ou que tu prépares des vacances en groupe, les catégories t’aident à :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Comprendre tes habitudes de dépenses&lt;/strong&gt; : voir où part vraiment l’argent commun&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Mieux prévoir&lt;/strong&gt; : t’appuyer sur ce que vous avez déjà dépensé pour budgéter les prochains événements du même genre&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Jouer la transparence&lt;/strong&gt; : tout le monde dans le groupe voit la répartition par catégorie&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;lance-toi&quot;&gt;Lance-toi&lt;/h2&gt;

&lt;p&gt;Les catégories sont disponibles dès maintenant dans la dernière version de Kittysplit. Ajoute ou modifie une dépense, et tu verras l’option pour choisir une catégorie. C’est aussi simple que ça !&lt;/p&gt;

&lt;p&gt;Comme toujours, on serait ravis d’avoir ton avis et tes idées d’amélioration.&lt;/p&gt;

&lt;p&gt;Bon partage ! 🐱✨&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;Une idée pour que les catégories collent mieux à ton usage ? Écris-nous sur &lt;a href=&quot;https://x.com/kittysplit&quot;&gt;X&lt;/a&gt;, &lt;a href=&quot;https://bsky.app/profile/kittysplit.bsky.social&quot;&gt;Bluesky&lt;/a&gt;, &lt;a href=&quot;https://www.instagram.com/kittysplit.app/&quot;&gt;Instagram&lt;/a&gt; ou par e-mail à info[at]kittysplit.com&lt;/em&gt;&lt;/p&gt;
</description>
        <pubDate>Wed, 11 Feb 2026 00:00:00 +0000</pubDate>
        <link>https://blog.kittysplit.com/fr/categories-de-depenses/</link>
        <guid isPermaLink="true">https://blog.kittysplit.com/fr/categories-de-depenses/</guid>
      </item>
    
      <item>
        <title>Nouveau tableau de bord et avatars de chats</title>
        <description>&lt;p&gt;Juste à temps pour Noël 2025, les applis Kittysplit ont droit à une mise à jour (v1.3), avec deux améliorations qu’on voulait faire depuis un moment :&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Un tableau de bord repensé&lt;/strong&gt;, qui montre l’essentiel sans te noyer sous les chiffres&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Des avatars de chats&lt;/strong&gt;, pour un look plus sympa et plus ludique&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;le-tableau-de-bord-avant-et-après&quot;&gt;Le tableau de bord, avant et après&lt;/h2&gt;

&lt;p&gt;Le tableau de bord, c’est ce que tu vois en premier quand tu ouvres une Kitty. On l’a retravaillé pour qu’il réponde vite aux questions principales : combien a coûté l’événement au total ? Qui doit combien ? Dans l’ensemble, on a essayé de mieux structurer tout ça grâce aux cartes de Material Design, et pas seulement sur le tableau de bord.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;&lt;img src=&quot;/images/2025-12-09/kittysplit_app_overview_v1.png&quot; alt=&quot;Tableau de bord de la Kitty - avant&quot; title=&quot;Tableau de bord de la Kitty - avant&quot; width=&quot;320&quot; /&gt;&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;&lt;img src=&quot;/images/2025-12-09/kittysplit_app_overview_v1.3_light.png&quot; alt=&quot;Tableau de bord de la Kitty - après&quot; title=&quot;Tableau de bord de la Kitty - après&quot; width=&quot;320&quot; /&gt;&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;em&gt;Tableau de bord de la Kitty - avant&lt;/em&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;em&gt;Tableau de bord de la Kitty - après&lt;/em&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;!-- read-more --&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;&lt;img src=&quot;/images/2025-12-09/kittysplit_app_overview_v1.3_dark.png&quot; alt=&quot;Tableau de bord de la Kitty - après (mode sombre)&quot; title=&quot;Tableau de bord de la Kitty - après (mode sombre)&quot; width=&quot;320&quot; /&gt;&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;&lt;img src=&quot;/images/2025-12-09/kittysplit_app_manage_participants_v1.3_dark.png&quot; alt=&quot;Gérer les participants - après (mode sombre)&quot; title=&quot;Gérer les participants - après (mode sombre)&quot; width=&quot;320&quot; /&gt;&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;em&gt;Tableau de bord de la Kitty - après (mode sombre)&lt;/em&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;em&gt;Gérer les participants - après (mode sombre)&lt;/em&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2 id=&quot;un-avatar-de-chat-pour-chaque-participant&quot;&gt;Un avatar de chat pour chaque participant&lt;/h2&gt;

&lt;p&gt;Dans chaque Kitty, chaque participant a maintenant son propre avatar de chat. Oui, ça va bien avec le nom Kittysplit, mais il y a aussi des raisons pratiques.&lt;/p&gt;

&lt;p&gt;Un repère visuel se lit plus vite que du texte seul. Grâce aux chats :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;l’appli ressemble moins à un tableur&lt;/li&gt;
  &lt;li&gt;on reconnaît plus facilement les participants dans les grands groupes&lt;/li&gt;
  &lt;li&gt;on voit tout de suite qui a payé quoi en faisant défiler les dépenses&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;&lt;img src=&quot;/images/2025-12-09/cat_avatars_sample.png&quot; alt=&quot;Avatars de chats (exemple)&quot; title=&quot;Avatars de chats (exemple)&quot; width=&quot;640&quot; /&gt;&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;em&gt;Avatars de chats (exemple)&lt;/em&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2 id=&quot;ce-qui-na-pas-changé&quot;&gt;Ce qui n’a pas changé&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Pas d’inscription nécessaire&lt;/li&gt;
  &lt;li&gt;Fonctionne sur le web, iOS et Android&lt;/li&gt;
  &lt;li&gt;Calcul malin des dettes&lt;/li&gt;
  &lt;li&gt;Gratuit pour la plupart des fonctionnalités&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;récupère-la-mise-à-jour&quot;&gt;Récupère la mise à jour&lt;/h2&gt;

&lt;p&gt;Sur mobile, la mise à jour arrive via l’App Store ou Google Play.&lt;/p&gt;

&lt;p&gt;Dis-nous ce que tu en penses à info[at]kittysplit.com ou via le bouton « Commentaires ».&lt;/p&gt;
</description>
        <pubDate>Tue, 09 Dec 2025 00:00:00 +0000</pubDate>
        <link>https://blog.kittysplit.com/fr/nouveau-tableau-de-bord-et-avatars-de-chats/</link>
        <guid isPermaLink="true">https://blog.kittysplit.com/fr/nouveau-tableau-de-bord-et-avatars-de-chats/</guid>
      </item>
    
      <item>
        <title>Kittysplit&apos;s RevenueCat Shipaton 2025 Story</title>
        <description>&lt;p&gt;We’re excited to share our story for the &lt;a href=&quot;https://www.shipaton.com&quot;&gt;RevenueCat Shipaton 2025&lt;/a&gt;! As part of this fun hackathon, we finally took the leap and shipped our long-awaited Android and iOS apps for Kittysplit.
The Shipaton challenged us to not just build something new, but to actually ship it and that’s exactly what we needed to push our mobile apps from “someday” to “today.” Below is the story we submitted for the hackathon, chronicling our journey from a simple side project born during a ski trip to launching our mobile apps.&lt;/p&gt;

&lt;!-- read-more --&gt;

&lt;h1 id=&quot;kittysplit-easy-splitting-of-group-expenses&quot;&gt;Kittysplit: Easy splitting of group expenses&lt;/h1&gt;

&lt;h2 id=&quot;inspiration&quot;&gt;Inspiration&lt;/h2&gt;

&lt;p&gt;Kittysplit started as a side project born from a real problem we faced during a ski trip with friends years ago. We were struggling to split our expenses fairly and efficiently. Excel felt clunky and overcomplicated for what should be a simple task. When we couldn’t find an existing tool that was truly easy to use, we decided to build our own.&lt;/p&gt;

&lt;p&gt;What began as a web-only solution has grown organically over the years. We’ve continuously improved the platform based on constant feedback from our users, who have been incredibly helpful in shaping what Kittysplit has become today. As our user base grew, one request kept coming up:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“When will you have mobile apps?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We started working on the apps when we had time, but participating in this hackathon gave us the final push to finish and ship them to the app stores.&lt;/p&gt;

&lt;h2 id=&quot;what-it-does&quot;&gt;What it does&lt;/h2&gt;

&lt;p&gt;Kittysplit solves one problem really well: making group expense splitting effortless. Whether you’re on vacation with friends, sharing household costs with roommates, or organizing a group dinner, our app provides the simplest way to calculate who owes what to whom. No overcomplicated features, no confusing interfaces: just straightforward expense splitting that anyone can understand.&lt;/p&gt;

&lt;h2 id=&quot;how-we-built-it&quot;&gt;How we built it&lt;/h2&gt;

&lt;p&gt;The journey to mobile started with building an API to integrate with our existing web application. Our initial plan was ambitious: use Kotlin Multiplatform for shared business logic, networking, local storage, and calculations, and build two separate user interfaces: a Jetpack Compose UI for Android, and a native SwiftUI frontend for iOS.&lt;/p&gt;

&lt;p&gt;Since we already had Android development experience, we began with the Android app and built the multiplatform part and the Compose UI for Android. We took great care to keep our client side business logic fully separate from the UI implementation by using ViewModels with StateFlow in Kotlin multiplatform. This allowed us to reuse all business logic for our iOS app. For the multiplatform payment system integration, we used RevenueCat - it turns out that Kittysplit and RevenueCat are a purrr-fect combination!&lt;/p&gt;

&lt;p&gt;Then we started working on the SwiftUI client. Initially, it seemed quite straightforward and using our KMP ViewModels from SwiftUI code worked really well. However during the process, it turned out to be a lot more work than expected. We also realized that building our UI in Compose was much easier than in SwiftUI, and that Compose Multiplatform would save us significant development time and effort. This pivot allowed us to share not just the business logic, but also most of the UI code between platforms.&lt;/p&gt;

&lt;h2 id=&quot;challenges-we-ran-into&quot;&gt;Challenges we ran into&lt;/h2&gt;

&lt;p&gt;Most of our challenges were product-focused rather than technical. The biggest question was how to translate the simple, intuitive flows that work well on the web into mobile app experiences. We spent considerable time ensuring that the mobile version maintained the same simplicity that makes our web app successful. Keeping the product simple sounds easy, but the big challenge is hiding the technical and mathematical complexity from the user and make it all work intuitively.&lt;/p&gt;

&lt;p&gt;On the technical side, we took great care to keep our client side business logic separate from the UI implementation by using ViewModels in Kotlin multiplatform. The main challenge was realizing that building and maintaining a separate iOS frontend in SwiftUI besides our existing backend, web UI and the Android Compose app was too much work for us, as a small team of 3 people working part-time on this project. So we ditched our half finished SwiftUI app even though we already spent a lot of time on it. The switch to Compose Multiplatform was easier than expected and crucial to get the iOS app released soon after Android.&lt;/p&gt;

&lt;h2 id=&quot;accomplishments-that-were-proud-of&quot;&gt;Accomplishments that we’re proud of&lt;/h2&gt;

&lt;p&gt;The user response has been incredibly encouraging. Already during the beta phase, we received a lot of positive feedback from users. Many of our existing web users are actively switching to the mobile apps, which validates that our concept translates well to mobile. And new users already say that after trying several established competitor’s apps, they are happy to have found the Kittysplit app.&lt;/p&gt;

&lt;p&gt;What makes us most proud is that users immediately understand how to use the app. The simplicity and intuitive design we worked so hard to achieve on the web has successfully carried over to mobile. Users can start splitting expenses within seconds of opening the app.&lt;/p&gt;

&lt;h2 id=&quot;what-we-learned&quot;&gt;What we learned&lt;/h2&gt;

&lt;p&gt;The technical transition from Android Compose to Compose Multiplatform turned out to be surprisingly smooth. We learned a lot about both technologies throughout the process.&lt;/p&gt;

&lt;p&gt;Perhaps the most interesting learning curve was diving into the iOS ecosystem for the first time. We spent considerable time learning Swift and SwiftUI, only to eventually abandon that code when we switched to Compose Multiplatform. While it felt like wasted effort initially, understanding iOS development principles helped us make better decisions for the multiplatform approach.&lt;/p&gt;

&lt;p&gt;We also learned the intricacies of iOS app publishing which was a completely new world for us as primarily Android developers.&lt;/p&gt;

&lt;h2 id=&quot;whats-next-for-kittysplit&quot;&gt;What’s next for Kittysplit&lt;/h2&gt;

&lt;p&gt;Our immediate focus is on App Store Optimization to grow our mobile user base.&lt;/p&gt;

&lt;p&gt;We’re committed to continuing our user feedback driven development approach. Our feature roadmap includes several highly requested additions:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Expense attachments (receipts, photos)&lt;/li&gt;
  &lt;li&gt;Expense categories for better organization&lt;/li&gt;
  &lt;li&gt;Push notifications to keep everyone updated&lt;/li&gt;
  &lt;li&gt;And much more!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal remains the same: keep Kittysplit the easiest way to split group expenses, whether you’re using our web app or mobile apps.&lt;/p&gt;
</description>
        <pubDate>Tue, 30 Sep 2025 00:00:00 +0000</pubDate>
        <link>https://blog.kittysplit.com/en/revenuecat-shipaton-story/</link>
        <guid isPermaLink="true">https://blog.kittysplit.com/en/revenuecat-shipaton-story/</guid>
      </item>
    
      <item>
        <title>Collecting Reviews in a Compose Multiplatform App</title>
        <description>&lt;p&gt;Shipping a great app is only half the battle, earning high quality reviews is just as crucial for App Store Optimization (ASO). Ratings and reviews shape first impressions, drive conversion, and influence store ranking. For a product like Kittysplit, where trust and simplicity are everything, timely and authentic user feedback helps future users decide to install, and helps us steer the roadmap.&lt;/p&gt;

&lt;p&gt;But asking for reviews is a delicate craft. Prompt too early and you interrupt the flow, prompt too often and you annoy users, prompt at the wrong moment and you get low‑quality or frustrated feedback. In this post, we’ll share how we integrated Google Play’s &lt;a href=&quot;https://developer.android.com/guide/playcore/in-app-review/kotlin-java&quot;&gt;ReviewManager&lt;/a&gt; and Apple’s &lt;a href=&quot;https://developer.apple.com/documentation/storekit/skstorereviewcontroller/&quot;&gt;SKStoreReviewController&lt;/a&gt; into our Compose Multiplatform app, and how we built a cross‑platform strategy to decide when to ask. We’ll cover the technical glue, the platform nuances, and the heuristics we use to surface the prompt at moments when users are most likely to feel satisfied, not interrupted.&lt;/p&gt;

&lt;!-- read-more --&gt;

&lt;h2 id=&quot;implementation&quot;&gt;Implementation&lt;/h2&gt;

&lt;p&gt;We’ll start with the implementation. We define a shared &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ReviewService&lt;/code&gt; API in common code and provide platform-specific implementations that wrap Android’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ReviewManager&lt;/code&gt; and iOS’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SKStoreReviewController&lt;/code&gt;. Using Kotlin Mutliplatform’s expect/actual mechanism in combination with &lt;a href=&quot;https://insert-koin.io&quot;&gt;Koin&lt;/a&gt;, we’re providing the correct implementation for each platform. This keeps the call site in our Compose Multiplatform UI identical across platforms, while letting each target handle its own prompt presentation in a native, compliant way.&lt;/p&gt;

&lt;p&gt;We’re using a freshly created Compose Multiplatform project as the base for this post. You can find a little more detailed description of how to create a new CMP project in our previous blog post &lt;a href=&quot;/using-firebase-analytics-in-cmp&quot;&gt;here&lt;/a&gt;. You can find all the code on &lt;a href=&quot;https://github.com/Kittysplit/ReviewServiceCMP&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;At first, we need to add a few dependencies to our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;libs.versions.toml&lt;/code&gt;. If you don’t know what that is, you can read up on it &lt;a href=&quot;https://developer.android.com/build/migrate-to-catalogs&quot;&gt;here&lt;/a&gt;. It’s a relatively new way of managing dependencies in gradle.&lt;/p&gt;

&lt;div class=&quot;language-toml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;versions&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# ... other verions&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;android-play-review&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2.0.2&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;koin&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;4.1.1&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;kotlinx-coroutines&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1.10.2&quot;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;libraries&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# ... other libraries&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;android-play-review&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;com.google.android.play:review&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ref&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;android-play-review&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;android-play-review-ktx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;com.google.android.play:review-ktx&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ref&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;android-play-review&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;koin-compose&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;io.insert-koin:koin-compose&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ref&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;koin&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;koin-core&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;io.insert-koin:koin-core&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ref&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;koin&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;kotlinx-coroutines-core&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;org.jetbrains.kotlinx:kotlinx-coroutines-core&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ref&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;kotlinx-coroutines&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;kotlinx-coroutines-play-services&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;org.jetbrains.kotlinx:kotlinx-coroutines-play-services&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ref&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;kotlinx-coroutines&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;./gradle/libs.versions.toml&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Next, we’re going to declare these dependencies in our compose app’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;build.gradle.kts&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;kotlin&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// ... other settings&lt;/span&gt;

    &lt;span class=&quot;nf&quot;&gt;sourceSets&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;androidMain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dependencies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// ... other android dependencies&lt;/span&gt;

            &lt;span class=&quot;nf&quot;&gt;implementation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;libs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;android&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;play&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;review&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;implementation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;libs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;android&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;play&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;review&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ktx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;implementation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;libs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kotlinx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;coroutines&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;play&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;commonMain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dependencies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// ... other common dependencies&lt;/span&gt;

            &lt;span class=&quot;nf&quot;&gt;implementation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;libs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;koin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;core&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;implementation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;libs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;koin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;compose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;implementation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;libs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kotlinx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;coroutines&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;core&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;./composeApp/build.gradle.kts&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now we need to define an interface in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;commonMain&lt;/code&gt; that we’ll implement later in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;androidMain&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;iosMain&lt;/code&gt; respectively:&lt;/p&gt;

&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ActivityContainer&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;EmptyActivityContainer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ActivityContainer&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ReviewResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Requested&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ReviewResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Failed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ReviewResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;data class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RequestError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ReviewResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ReviewService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;suspend&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;requestInAppReview&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;activityContainer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ActivityContainer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;EmptyActivityContainer&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ReviewResult&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;./composeApp/src/commonMain/kotlin/com/kittysplit/reviewservicecmp/ReviewService.kt&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Because Apple’s SKStoreReviewController and Google Play’s ReviewManager don’t expose whether a dialog was actually shown or acted upon, our API only reports &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Requested&lt;/code&gt; or an error. This keeps our contract honest and portable across both stores’ opaque review flows. Don’t mind the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActivityContainer&lt;/code&gt; interface and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EmptyActivityContainer&lt;/code&gt; object, we’re going to explain that soon.&lt;/p&gt;

&lt;h2 id=&quot;android&quot;&gt;Android&lt;/h2&gt;

&lt;p&gt;Let’s look at the implementation for Android:&lt;/p&gt;

&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;android.app.Activity&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;android.content.Context&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;com.google.android.play.core.review.ReviewException&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;com.google.android.play.core.review.ReviewManagerFactory&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;kotlinx.coroutines.tasks.await&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;data class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;AndroidContextHolder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;activity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Activity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ActivityContainer&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ActivityContainer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;activity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Activity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;AndroidContextHolder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;activity&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;AndroidReviewService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ReviewService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;reviewManager&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ReviewManagerFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;suspend&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;requestInAppReview&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;activityContainer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ActivityContainer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ReviewResult&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;activity&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;activityContainer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;activity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;activity&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kd&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;reviewInfo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reviewManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;requestReviewFlow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;await&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;reviewManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;launchReviewFlow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;activity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reviewInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;await&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;ReviewResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Requested&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;ReviewResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Failed&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ReviewException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;ReviewResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;RequestError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;errorCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;ReviewResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Failed&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;./composeApp/src/androidMain/kotlin/com/kittysplit/reviewservicecmp/AndroidReviewService.kt&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We implemented the Android side of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ReviewService&lt;/code&gt; using Play Core’s in‑app review flow. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AndroidReviewService&lt;/code&gt; creates a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ReviewManager&lt;/code&gt;, requests a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ReviewInfo&lt;/code&gt; using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;requestReviewFlow().await()&lt;/code&gt;, and then launches the prompt with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reviewManager.launchReviewFlow(...).await()&lt;/code&gt;. Because &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;launchReviewFlow&lt;/code&gt; requires an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Activity&lt;/code&gt;, not a generic &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Context&lt;/code&gt;, we created a small container object to be able to hand over the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Activity&lt;/code&gt; in the Android implementation. For iOS, we’re going to use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EmptyActivityContainer&lt;/code&gt;, since we don’t need to pass anything there.&lt;/p&gt;

&lt;p&gt;If something goes wrong on Android, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ReviewManager&lt;/code&gt; might throw a &lt;a href=&quot;https://developer.android.com/reference/com/google/android/play/core/review/ReviewException&quot;&gt;ReviewException&lt;/a&gt;. In case we encounter it, we’re extracting the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;errorCode&lt;/code&gt; and return it as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ReviewResult.RequestError&lt;/code&gt;. This allows you to handle these errors separately from the generic &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ReviewResult.Failed&lt;/code&gt;. For possible values of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;errorCode&lt;/code&gt; see the documentation &lt;a href=&quot;https://developer.android.com/reference/com/google/android/play/core/review/model/ReviewErrorCode&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;ios&quot;&gt;iOS&lt;/h2&gt;

&lt;p&gt;Next, we’re going to implement the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AppleReviewService&lt;/code&gt; in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;iosMain&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;platform.StoreKit.SKStoreReviewController&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;platform.UIKit.UIApplication&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;platform.UIKit.UISceneActivationStateForegroundActive&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;platform.UIKit.UIWindowScene&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;AppleReviewService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ReviewService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;suspend&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;requestInAppReview&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;activityContainer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ActivityContainer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ReviewResult&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;scene&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;UIApplication&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sharedApplication&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;connectedScenes&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filterIsInstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;UIWindowScene&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;firstOrNull&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;activationState&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;UISceneActivationStateForegroundActive&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;scene&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;SKStoreReviewController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;requestReviewInScene&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;scene&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;SKStoreReviewController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;requestReview&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;ReviewResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Requested&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;ReviewResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Failed&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;./composeApp/src/iosMain/kotlin/com/kittysplit/reviewservicecmp/AppleReviewService.kt&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We implemented the iOS side with StoreKit’s in‑app review API. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AppleReviewService&lt;/code&gt; looks up the currently active &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UIWindowScene&lt;/code&gt; by scanning &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UIApplication.sharedApplication.connectedScenes&lt;/code&gt; and picking the one in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UISceneActivationStateForegroundActive&lt;/code&gt; state. If an active scene exists, we call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SKStoreReviewController.requestReviewInScene(scene)&lt;/code&gt;. Otherwise we fall back to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SKStoreReviewController.requestReview()&lt;/code&gt;. Both calls are “fire‑and‑forget” (no success callback), so we return &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ReviewResult.Requested&lt;/code&gt; after invoking them. Any exception during lookup or invocation maps to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ReviewResult.Failed&lt;/code&gt;. This approach ensures the prompt is tied to the foreground scene in multi‑window setups while remaining compatible with older, scene‑agnostic code paths.&lt;/p&gt;

&lt;h2 id=&quot;koin&quot;&gt;Koin&lt;/h2&gt;

&lt;p&gt;That’s it. All we need to do now is wire it all together to be able to use it in our common app code. We’re going to use &lt;a href=&quot;https://insert-koin.io&quot;&gt;Koin&lt;/a&gt; for that, but you can go with any other DI framework.&lt;/p&gt;

&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;org.koin.core.module.Module&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;expect&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;reviewModule&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Module&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;./composeApp/src/commonMain/kotlin/com/kittysplit/reviewservicecmp/ReviewModule.kt&lt;/em&gt;&lt;/p&gt;

&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;org.koin.core.module.Module&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;org.koin.core.module.dsl.singleOf&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;org.koin.dsl.module&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;actual&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;reviewModule&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Module&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;singleOf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;AndroidReviewService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;./composeApp/src/androidMain/kotlin/com/kittysplit/reviewservicecmp/ReviewModule.android.kt&lt;/em&gt;&lt;/p&gt;

&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;org.koin.core.module.Module&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;org.koin.core.module.dsl.singleOf&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;org.koin.dsl.module&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;actual&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;reviewModule&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Module&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;singleOf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;AppleReviewService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;./composeApp/src/iosMain/kotlin/com/kittysplit/reviewservicecmp/ReviewModule.ios.kt&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We’re not going in detail here, but it should be straight forward. We’re defining a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reviewModule()&lt;/code&gt; as an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;expect&lt;/code&gt; function and provide the platform-specific implementations in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;actual&lt;/code&gt; counterparts.&lt;/p&gt;

&lt;p&gt;With both implementations in place, you can inject &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ReviewService&lt;/code&gt; into any ViewModel and trigger review requests at the right moments in your user flows. Pick thoughtful triggers, wire them to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;requestInAppReview&lt;/code&gt;, and you’re ready to ask for reviews without disrupting your users.&lt;/p&gt;

&lt;h2 id=&quot;review-triggers&quot;&gt;Review triggers&lt;/h2&gt;

&lt;p&gt;We gate review prompts behind lightweight heuristics so they appear at moments of success, not interruption. We track when the app was first opened, total opens, when we last requested a review, and how many expenses a user has added. We use two trigger events: when a user adds an expense and when they settle up. We prioritized the settle-up event because it aligns with a clear success moment. Our heuristics are:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Last review request ≥ x days ago&lt;/li&gt;
  &lt;li&gt;First app open ≥ x days ago&lt;/li&gt;
  &lt;li&gt;App opened ≥ x times&lt;/li&gt;
  &lt;li&gt;Current Kitty has ≥ x expenses&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We apply these on both platforms, but we’re more conservative on iOS, prompting less frequently and only after stronger engagement because the app is only three weeks old, and because Apple only displays the review prompt maximum &lt;a href=&quot;https://developer.apple.com/documentation/storekit/requesting-app-store-reviews&quot;&gt;3 times a year&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Collecting reviews is both a technical integration and a product judgment call. With a shared &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ReviewService&lt;/code&gt; and platform-native implementations for Android’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ReviewManager&lt;/code&gt; and iOS’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SKStoreReviewController&lt;/code&gt;, you now have a clean way to prompt your users for reviews from your common code. Pair that with thoughtful heuristics that favor genuine success moments, and you’ll nudge happy users to share feedback without breaking their flow, ultimately improving ASO, conversion, and the product itself.&lt;/p&gt;

&lt;p&gt;If you try this approach in your own Compose Multiplatform app, we’d love to hear what heuristics work best for you. Feels free to contact us on social networks (&lt;a href=&quot;https://x.com/kittysplit&quot;&gt;X&lt;/a&gt;, &lt;a href=&quot;https://bsky.app/profile/kittysplit.bsky.social&quot;&gt;Bluesky&lt;/a&gt;, &lt;a href=&quot;https://www.instagram.com/kittysplit.app/&quot;&gt;Instagram&lt;/a&gt;) or write an email.&lt;/p&gt;
</description>
        <pubDate>Fri, 26 Sep 2025 00:00:00 +0000</pubDate>
        <link>https://blog.kittysplit.com/en/collect_reviews_in_a_compose_multiplatform_app/</link>
        <guid isPermaLink="true">https://blog.kittysplit.com/en/collect_reviews_in_a_compose_multiplatform_app/</guid>
      </item>
    
      <item>
        <title>Firebase Analytics in Compose Multiplatform without Cocoapods</title>
        <description>&lt;p&gt;At Kittysplit, we’ve always aimed to make splitting group expenses as effortless as possible. For years, Kittysplit was a web first product. As our users took Kittysplit on trips, into shared apartments, and to events, it became clear that we needed &lt;a href=&quot;/mobile-app-release/&quot;&gt;mobile apps&lt;/a&gt; on both Android and iOS.&lt;/p&gt;

&lt;p&gt;In this post, we’ll provide a hands-on guide how we wired Firebase Analytics into our Compose Multiplatform app on both Android and iOS without using Cocoapods (which is going to &lt;a href=&quot;https://blog.cocoapods.org/CocoaPods-Specs-Repo/&quot;&gt;retire&lt;/a&gt; soon).&lt;/p&gt;

&lt;!-- read-more --&gt;

&lt;h2 id=&quot;from-swiftui-to-compose-multiplatform&quot;&gt;From SwiftUI to Compose Multiplatform&lt;/h2&gt;

&lt;p&gt;We embraced Kotlin Multiplatform to share business logic across platforms, initially pairing it with a native Android app and a SwiftUI app for iOS, each with its own &lt;a href=&quot;https://firebase.google.com/docs/analytics&quot;&gt;Firebase Analytics&lt;/a&gt; integration.&lt;/p&gt;

&lt;p&gt;But when we decided to &lt;a href=&quot;/building-kittysplit-apps-in-cmp/&quot;&gt;fully commit to Compose Multiplatform&lt;/a&gt; and retire our SwiftUI app, we hit a wall: there’s no out of the box way to use Firebase Analytics in a Compose Multiplatform project. Also, Cocoapods is going to &lt;a href=&quot;https://blog.cocoapods.org/CocoaPods-Specs-Repo/&quot;&gt;retire&lt;/a&gt; soon. Instead, we’ll use the Swift Package Manager dependencies directly with the wonderful &lt;a href=&quot;https://github.com/frankois944/spm4Kmp&quot;&gt;spm4Kmp&lt;/a&gt; plugin made by &lt;a href=&quot;https://github.com/frankois944&quot;&gt;François&lt;/a&gt;. We’re also going to use &lt;a href=&quot;https://insert-koin.io&quot;&gt;Koin&lt;/a&gt; to wire it all together.&lt;/p&gt;

&lt;h2 id=&quot;project-setup&quot;&gt;Project setup&lt;/h2&gt;

&lt;p&gt;We’re going to add Firebase Analytics to a newly created Compose Multiplatform project, which we’re creating using the new project wizard in Android Studio. If you want to follow along, you can either check out the &lt;a href=&quot;https://github.com/Kittysplit/FirebaseAnalyticsCMP&quot;&gt;project code&lt;/a&gt; from GitHub or create a new project yourself.&lt;/p&gt;

&lt;p&gt;To create a new CMP project in Android Studio, make sure you have the &lt;a href=&quot;https://plugins.jetbrains.com/plugin/14936-kotlin-multiplatform&quot;&gt;Kotlin Multiplatform plugin&lt;/a&gt; installed. You can then click &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;File -&amp;gt; New -&amp;gt; New Project...&lt;/code&gt;. Under “Phone and Tablet” select “Kotlin Multiplatform”, enter a name and package for your app and click “Next”. On the next screen, make sure you have the Android and iOS boxes ticked and select “Share UI” as UI implementation. Click “Finish” to create the new project.&lt;/p&gt;

&lt;h2 id=&quot;dependencies&quot;&gt;Dependencies&lt;/h2&gt;

&lt;p&gt;Our first step is to add the needed dependencies. To do this, we’re going to use the &lt;a href=&quot;https://docs.gradle.org/current/userguide/version_catalogs.html&quot;&gt;gradle version catalog&lt;/a&gt;. Locate the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;libs.versions.toml&lt;/code&gt; file and add the following lines:&lt;/p&gt;

&lt;div class=&quot;language-toml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;versions&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# ... other dependencies&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;koin&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;4.1.1&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;spm4kmp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1.0.0-Beta04&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;firebase-analytics&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;22.5.0&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;google-services&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;4.4.3&quot;&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# spm&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;spm-firebase&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;12.0.0&quot;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;libraries&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# ... other libraries&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;koin-compose&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;io.insert-koin:koin-compose&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ref&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;koin&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;koin-core&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;io.insert-koin:koin-core&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ref&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;koin&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;firebase-analytics-ktx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;com.google.firebase:firebase-analytics-ktx&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ref&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;firebase-analytics&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;plugins&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# ... other plugins&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;spm4kmp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;io.github.frankois944.spmForKmp&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ref&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;spm4kmp&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;google-services&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;com.google.gms.google-services&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ref&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;google-services&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;./gradle/libs.versions.toml&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You can now sync your project with the gradle files to pull in the dependencies. With this out of the way, we’re going to declare the dependencies in our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;build.gradle.kts&lt;/code&gt; files. We’ll start with the root &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;build.gradle.kts&lt;/code&gt; file. Add this line:&lt;/p&gt;

&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;plugins&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// other plugins&lt;/span&gt;

    &lt;span class=&quot;nf&quot;&gt;alias&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;libs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;plugins&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;spm4kmp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;apply&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;alias&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;libs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;plugins&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;google&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;apply&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;./build.gradle.kts&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This just tells gradle to resolve the plugin, but not apply it. This is useful if you have a multi-module gradle project and only want to apply the plugin in a single submodule.&lt;/p&gt;

&lt;p&gt;Now we’re going to set up the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gradle.build.kts&lt;/code&gt; of our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;composeApp&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;plugins&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// other plugins&lt;/span&gt;

    &lt;span class=&quot;nf&quot;&gt;alias&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;libs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;plugins&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;spm4kmp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;alias&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;libs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;plugins&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;google&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;


&lt;span class=&quot;nf&quot;&gt;kotlin&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;

    &lt;span class=&quot;nf&quot;&gt;listOf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;iosArm64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;iosSimulatorArm64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;forEach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;iosTarget&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;iosTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;binaries&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;framework&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;baseName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;ComposeApp&quot;&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;binaryOption&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;bundleId&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;com.kittysplit.firebase_analytics_cmp&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;isStatic&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// This is the important part:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;iosTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;compilations&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kd&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;main&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;by&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getting&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;cinterops&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;nativeIosShared&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nf&quot;&gt;sourceSets&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;named&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;lowercase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;startsWith&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ios&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;configureEach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;languageSettings&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;nf&quot;&gt;optIn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;kotlinx.cinterop.ExperimentalForeignApi&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;androidMain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dependencies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;implementation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;libs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;firebase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;analytics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ktx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;commonMain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dependencies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;

            &lt;span class=&quot;nf&quot;&gt;api&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;libs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;koin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;compose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;api&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;libs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;koin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;core&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nf&quot;&gt;swiftPackageConfig&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;nativeIosShared&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;minIos&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;16.0&quot;&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;dependency&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;nf&quot;&gt;remotePackageVersion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://github.com/firebase/firebase-ios-sdk.git&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;products&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;FirebaseAnalytics&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exportToKotlin&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;FirebaseCore&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exportToKotlin&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;packageName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;firebase-ios-sdk&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;version&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;libs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;versions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;spm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;firebase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;./composeApp/build.gradle.kts&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;That’s all dependency juggling. We added koin as “regular” multiplatform dependency of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;commonMain&lt;/code&gt;. For the Android implementation of Firebase Analytics, we added the Android library to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;androidMain&lt;/code&gt;. To be able to use the iOS Firebase Analytics in our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;iosMain&lt;/code&gt; implementation, we used &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;spm4Kmp&lt;/code&gt; to declare the dependency. Notice the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exportToKotlin = true&lt;/code&gt;, this tells &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;spm4Kmp&lt;/code&gt; to export the dependency to kotlin, which means we can use it directly in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;iosMain&lt;/code&gt; code.&lt;/p&gt;

&lt;p&gt;When you sync the project again, you’ll notice a new folder: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;./composeApp/exportedNativeIosShared&lt;/code&gt;. This is the generated swift package which we’re going to add as a dependency to the iOS app next. Open XCode and navigate to the project dependencies:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025-09-23-using-firebase-analytics-in-cmp--xcode-add-dependency.png&quot; alt=&quot;XCode&quot; /&gt;
&lt;em&gt;XCode: iosApp -&amp;gt; PROJECT (iosApp) -&amp;gt; Package Dependencies&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Click on the little “+” sign and then on “Add Local…”. Now select the aforementioned folder and click “Add package”. Once done, you’ll see all dependencies of Firebase Analytics and Firebase itself in your package dependencies on the lower left of your XCode window.&lt;/p&gt;

&lt;h2 id=&quot;firebase-setup&quot;&gt;Firebase setup&lt;/h2&gt;

&lt;p&gt;To function properly, you need to register your Android and iOS Apps with Firebase. We’re not going to explain this in detail in this blogpost. Essentially, you need to provide a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GoogleService-Info.plist&lt;/code&gt; file on iOS and a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;google-services.json&lt;/code&gt; file on Android. You can find an in depth guide for Android &lt;a href=&quot;https://firebase.google.com/docs/android/setup&quot;&gt;here&lt;/a&gt;. The guide for iOS is located &lt;a href=&quot;https://firebase.google.com/docs/ios/setup&quot;&gt;here&lt;/a&gt;. Please note that you only need to follow the steps to obtain the aforementioned files, all other parts are covered in this blog post.&lt;/p&gt;

&lt;h2 id=&quot;implementation&quot;&gt;Implementation&lt;/h2&gt;

&lt;p&gt;This completes the boring part. We’re going to look at the implementation next. Our goal is to create a simple interface that allows us to track events to Firebase Analytics from view models, but also from Composables.&lt;/p&gt;

&lt;p&gt;We’re starting by creating a simple interface inside &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;commonMain&lt;/code&gt;, which we’re going to implement in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;androidMain&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;iosMain&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;package&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;com.kittysplit.firebase_analytics_cmp.analytics&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Analytics&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;logEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;?)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;./composeApp/src/commonMain/kotlin/com/kittysplit/firebase_analytics_cmp/analytics/Analytics.kt&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;For the Android implementation, we’re going to use the kotlin Firebase SDK in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;androidMain&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;package&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;com.kittysplit.firebase_analytics_cmp.analytics&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;androidx.core.os.bundleOf&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;com.google.firebase.analytics.FirebaseAnalytics&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;com.google.firebase.analytics.ktx.analytics&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;com.google.firebase.ktx.Firebase&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;AndroidAnalytics&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Analytics&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;lateinit&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;firebaseAnalytics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;FirebaseAnalytics&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;firebaseAnalytics&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Firebase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;analytics&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;logEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;?&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;args&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;toTypedArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;bundleOf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;firebaseAnalytics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;logEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;./composeApp/src/androidMain/kotlin/com/kittysplit/firebase_analytics_cmp/analytics/AndroidAnalytics.kt&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;As you can see, the implementation is straight forward. All we have to do is to convert our parameter map to a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Bundle&lt;/code&gt;, which is often used in Android to pass data around.&lt;/p&gt;

&lt;p&gt;On to the iOS implementation. We’re going to use the native iOS Firebase Analytics SDK in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;iosMain&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;package&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;com.kittysplit.firebase_analytics_cmp.analytics&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;FirebaseAnalytics.FIRAnalytics&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;FirebaseCore.FIRApp&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;AppleAnalytics&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Analytics&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;firAnalytics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;FIRAnalytics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Companion&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;FIRAnalytics&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;FIRApp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;configure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;logEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;?&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;firAnalytics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;logEventWithName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;params&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?,&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;?)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;./composeApp/src/iosMain/kotlin/com/kittysplit/firebase_analytics_cmp/analytics/AppleAnalytics.kt&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;dependency-injection&quot;&gt;Dependency injection&lt;/h2&gt;

&lt;p&gt;All that is left to do now is to wire it all up using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;koin&lt;/code&gt;. We’ll have to create a module that provides our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AppleAnalytics&lt;/code&gt; if we’re running on iOS and the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AndroidAnalytics&lt;/code&gt; implementation if we’re running on Android. That’s where kotlin’s easy to use [expect/actual declarations] https://www.jetbrains.com/help/kotlin-multiplatform-dev/multiplatform-expect-actual.html come in.&lt;/p&gt;

&lt;p&gt;In &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;commonMain&lt;/code&gt;, we’re only declaring our module as an expect function, which is comparable to an interface where the implementation are provided per platform:&lt;/p&gt;

&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;package&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;com.kittysplit.firebase_analytics_cmp.analytics&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;org.koin.core.module.Module&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;expect&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;analyticsModule&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Module&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;./composeApp/src/commonMain/kotlin/com/kittysplit/firebase_analytics_cmp/analytics/AnalyticsModule.kt&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Next, we’re providing the implementations for Android:&lt;/p&gt;

&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;package&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;com.kittysplit.firebase_analytics_cmp.analytics&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;org.koin.core.module.Module&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;org.koin.dsl.module&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;actual&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;analyticsModule&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Module&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;single&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Analytics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;analytics&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;AndroidAnalytics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;analytics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;analytics&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;./composeApp/src/androidMain/kotlin/com/kittysplit/firebase_analytics_cmp/analytics/AnalyticsModule.android.kt&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;And iOS:&lt;/p&gt;

&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;package&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;com.kittysplit.firebase_analytics_cmp.analytics&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;org.koin.core.module.Module&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;org.koin.dsl.module&lt;/span&gt;


&lt;span class=&quot;n&quot;&gt;actual&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;analyticsModule&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Module&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;single&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Analytics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;analytics&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;AppleAnalytics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;analytics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;analytics&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;./composeApp/src/iosMain/kotlin/com/kittysplit/firebase_analytics_cmp/analytics/AnalyticsModule.ios.kt&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;It’s important to call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;init&lt;/code&gt; on our implementations, to make sure they’re ready to go when they’re injected. Now we have to make sure that koin knows about the module we’ve just created. We’re going to do that by starting koin when our application starts. On Android, we can easily do that in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MainActivity&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;package&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;com.kittysplit.firebase_analytics_cmp&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;android.os.Bundle&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;androidx.activity.ComponentActivity&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;androidx.activity.compose.setContent&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;androidx.activity.enableEdgeToEdge&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;androidx.compose.runtime.Composable&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;androidx.compose.ui.tooling.preview.Preview&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;com.kittysplit.firebase_analytics_cmp.analytics.analyticsModule&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;org.koin.compose.KoinApplication&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MainActivity&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ComponentActivity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;onCreate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;savedInstanceState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Bundle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;enableEdgeToEdge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;onCreate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;savedInstanceState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;nf&quot;&gt;setContent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;KoinApplication&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;application&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;nf&quot;&gt;modules&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;analyticsModule&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;nc&quot;&gt;App&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;./composeApp/src/androidMain/kotlin/com/kittysplit/firebase_analytics_cmp/MainActivity.kt&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;To make it work on iOS, we need to start the koin application in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MainViewController&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;package&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;com.kittysplit.firebase_analytics_cmp&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;androidx.compose.ui.window.ComposeUIViewController&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;com.kittysplit.firebase_analytics_cmp.analytics.analyticsModule&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;org.koin.compose.KoinApplication&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MainViewController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ComposeUIViewController&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;KoinApplication&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;application&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;modules&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;analyticsModule&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;App&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;./composeApp/src/iosMain/kotlin/com/kittysplit/firebase_analytics_cmp/MainViewController.kt&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now we’re ready to inject our analytics implementation anywhere we need it. To test it, we’re going to use it to track the tap on the “Click me!” button from the example:&lt;/p&gt;

&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;package&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;com.kittysplit.firebase_analytics_cmp&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// ... imports&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;com.kittysplit.firebase_analytics_cmp.analytics.Analytics&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;org.koin.compose.koinInject&lt;/span&gt;

&lt;span class=&quot;nd&quot;&gt;@Composable&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;App&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;analytics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Analytics&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;koinInject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;MaterialTheme&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;showContent&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;by&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;remember&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;mutableStateOf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;Column&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;modifier&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Modifier&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;MaterialTheme&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;colorScheme&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;primaryContainer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;safeContentPadding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fillMaxSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;horizontalAlignment&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Alignment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;CenterHorizontally&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;onClick&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;analytics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;logEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;button_clicked&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;showContent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;showContent&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;nc&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Click me!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;AnimatedVisibility&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;showContent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;kd&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;greeting&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;remember&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Greeting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;greet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;nc&quot;&gt;Column&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;modifier&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Modifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fillMaxWidth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;horizontalAlignment&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Alignment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;CenterHorizontally&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;nc&quot;&gt;Image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;painterResource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;drawable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;compose_multiplatform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;nc&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Compose: $greeting&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;./composeApp/src/commonMain/kotlin/com/kittysplit/firebase_analytics_cmp/App.kt&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;That’s it! You’re now ready to track events from anywhere in your Android Compose app. There’s one little thing we need to fix. You might have noticed that your preview is not showing any longer. That’s because koin is not initialized. Luckily, there’s a fix for that: we’re going to provide a fake instance of our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Analytics&lt;/code&gt; for previews:&lt;/p&gt;

&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;package&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;com.kittysplit.firebase_analytics_cmp.analytics&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;org.koin.dsl.module&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;noopAnalyticsModule&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;single&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Analytics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Analytics&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;logEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;?)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;./composeApp/src/commonMain/kotlin/com/kittysplit/firebase_analytics_cmp/analytics/NoopAnalyticsModule.kt&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now all we have to do is wrap our previews with a little helper:&lt;/p&gt;

&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nd&quot;&gt;@Preview&lt;/span&gt;
&lt;span class=&quot;nd&quot;&gt;@Composable&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AppPreview&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;KoinApplicationPreview&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;application&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;modules&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;noopAnalyticsModule&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;App&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now you should see the preview again.&lt;/p&gt;

&lt;p&gt;We hope this little guide helps you in your multiplatform endeavours. We had a lot of fun implementing the Kittysplit apps in Compose Multiplatform.&lt;/p&gt;
</description>
        <pubDate>Tue, 23 Sep 2025 00:00:00 +0000</pubDate>
        <link>https://blog.kittysplit.com/en/using-firebase-analytics-in-cmp/</link>
        <guid isPermaLink="true">https://blog.kittysplit.com/en/using-firebase-analytics-in-cmp/</guid>
      </item>
    
      <item>
        <title>Building Kittysplit&apos;s Mobile Apps in Compose Multiplatform</title>
        <description>&lt;p&gt;After 13 years as a web only application, Kittysplit has finally made the leap to mobile. We initially planned to build separate Android and iOS apps using Kotlin Multiplatform for shared business logic, but Compose Multiplatform’s recent stability changed everything. Here’s how we went from web only to native mobile apps.&lt;/p&gt;

&lt;!-- read-more --&gt;

&lt;h2 id=&quot;from-web-only-to-mobile-a-13-year-journey&quot;&gt;From Web only to Mobile: A 13 Year Journey&lt;/h2&gt;

&lt;p&gt;Kittysplit started as a web application back in 2012. For over a decade, we maintained it exclusively as a webapp. The web platform allowed us to provide universal accessibility. Anyone could create or join a Kitty simply by opening a link in their browser, regardless of their device type. This cross platform compatibility is crucial for group expense sharing, where participants often use different devices and operating systems.&lt;/p&gt;

&lt;h2 id=&quot;growing-demand-for-native-apps&quot;&gt;Growing Demand for Native Apps&lt;/h2&gt;

&lt;p&gt;However, user requests for dedicated mobile apps grew year after year. We felt that we needed to address this demand, so we looked at the available options for the implementation. We evaluated &lt;a href=&quot;https://reactnative.dev&quot;&gt;React Native&lt;/a&gt;, &lt;a href=&quot;https://flutter.dev&quot;&gt;Flutter&lt;/a&gt;, &lt;a href=&quot;https://kotlinlang.org/docs/multiplatform.html&quot;&gt;Kotlin Multiplatform&lt;/a&gt;, and &lt;a href=&quot;https://www.jetbrains.com/compose-multiplatform/&quot;&gt;Compose Multiplatform&lt;/a&gt; to determine the best approach for our small development team.&lt;/p&gt;

&lt;p&gt;Because we already had experience with Android development, we chose Kotlin Multiplatform as our foundation. Our initial plan was straightforward: develop the Android app first using &lt;a href=&quot;https://developer.android.com/compose&quot;&gt;Android Compose&lt;/a&gt;, while implementing all business logic in a shared module in Kotlin Multiplatform. This would allow us to share the core functionality across platforms: networking, business logic, calculations and local storage. We planned to build the iOS app using SwiftUI after completing the Android app, leveraging the already written shared business logic.&lt;/p&gt;

&lt;h2 id=&quot;the-power-of-kotlin-multiplatform&quot;&gt;The Power of Kotlin Multiplatform&lt;/h2&gt;

&lt;p&gt;Kotlin Multiplatform proved to be an excellent choice for our architecture. We implemented all networking, local storage, and client-side calculations in the shared multiplatform code. Additionally, we moved all our ViewModels into the shared codebase, ensuring consistent business logic and state management across both platforms.&lt;/p&gt;

&lt;p&gt;The implementation was straightforward: Kotlin provided almost everything we needed out of the box. For local data storage, we used &lt;a href=&quot;https://sqldelight.github.io/sqldelight/&quot;&gt;SQLDelight&lt;/a&gt;, which allowed us to write type-safe SQL queries. We integrated the &lt;a href=&quot;https://www.revenuecat.com/docs/getting-started/installation/kotlin-multiplatform&quot;&gt;RevenueCat Purchases multiplatform SDK&lt;/a&gt; for handling Super Kitty upgrades. To enhance the Swift interoperability for our planned iOS development, we used &lt;a href=&quot;https://skie.touchlab.co&quot;&gt;SKIE&lt;/a&gt; from TouchLab. For dependency injection, we chose &lt;a href=&quot;https://insert-koin.io&quot;&gt;Koin&lt;/a&gt;, which worked seamlessly across our multiplatform setup. This foundation gave us confidence that we could maintain a single source of truth for our core functionality while building native experiences on each platform.&lt;/p&gt;

&lt;h2 id=&quot;compose-multiplatform-goes-stable&quot;&gt;Compose Multiplatform Goes Stable&lt;/h2&gt;

&lt;p&gt;As it’s often the case in software development, the SwiftUI frontend turned out to be more work than we initially anticipated 🙈. We had been following Compose Multiplatform’s development for some time. Given the challenges we were facing with SwiftUI, we decided to experiment with it for a few days. Compose Multiplatform for iOS had recently reached stable status in May 2025.&lt;/p&gt;

&lt;p&gt;We managed to migrate our app to Compose Multiplatform within just a few days, which was much faster than we expected. The resulting app ran smoothly on our iOS devices and felt really well.&lt;/p&gt;

&lt;p&gt;This led us to ditch the native SwiftUI frontend entirely and go with Compose Multiplatform instead. It would save us months of work, both now and in the future. With only one codebase to maintain, we can spend our time improving the app and adding new features rather than implementing everything twice.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025-09-18-building-kittysplit-apps-in-cmp--kittysplit-ios-fte-screenshot.png&quot; alt=&quot;Kittysplit first time on iOS&quot; height=&quot;600&amp;quot;, style=&amp;quot;float: left&quot; /&gt;
&lt;img src=&quot;/images/2025-09-18-building-kittysplit-apps-in-cmp--kittysplit-ios-add-entry-screenshot.png&quot; alt=&quot;Kittysplit add entry on iOS&quot; height=&quot;600&amp;quot;, style=&amp;quot;float: right&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Kittysplit on iOS&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;the-migration-process-from-android-compose-to-compose-multiplatform&quot;&gt;The Migration Process: From Android Compose to Compose Multiplatform&lt;/h2&gt;

&lt;p&gt;The actual migration process was surprisingly straightforward. We started by importing the new Compose Multiplatform dependencies and migrating to multiplatform resources, which was mostly just copying a few files to the new structure. The main code changes involved removing usages of the Android Context throughout our codebase.&lt;/p&gt;

&lt;p&gt;Fortunately, we had already been using multiplatform libraries from Sentry and RevenueCat, so those integrations continued to work without any changes. For Firebase Analytics, we used the expect/actual mechanism to provide platform-specific implementations. We also needed to make our deep links work on both platforms, but the patterns were similar enough that it didn’t require major restructuring.&lt;/p&gt;

&lt;p&gt;Most of our existing Compose UI code worked with minimal adjustments, and the shared ViewModels we had already implemented in Kotlin Multiplatform integrated seamlessly. Within just a few days, we had a fully functional iOS app that felt genuinely good.&lt;/p&gt;

&lt;h2 id=&quot;looking-forward&quot;&gt;Looking Forward&lt;/h2&gt;

&lt;p&gt;We’re excited about the future of Kittysplit’s mobile apps with this technology stack. Kotlin Multiplatform and Compose Multiplatform have given us the perfect balance of development efficiency and native performance. With a single codebase to maintain, we can focus on what matters most: building features that make splitting expenses even easier for our users. We’re already working on several improvements and new features that would have taken us much longer to implement across separate platforms. The mobile app journey has finally found its ideal technical foundation, and we couldn’t be more excited about where it’s headed.&lt;/p&gt;
</description>
        <pubDate>Thu, 18 Sep 2025 00:00:00 +0000</pubDate>
        <link>https://blog.kittysplit.com/en/building-kittysplit-apps-in-cmp/</link>
        <guid isPermaLink="true">https://blog.kittysplit.com/en/building-kittysplit-apps-in-cmp/</guid>
      </item>
    
      <item>
        <title>Kittysplit passe au mobile&amp;nbsp;: voici nos applis Android et iOS</title>
        <description>&lt;p&gt;Après plus de dix ans uniquement sur le web, Kittysplit est enfin disponible en applis natives pour Android et iOS ! Depuis notre lancement en 2012, on aide les amis, les colocs et les groupes de voyageurs à partager leurs dépenses équitablement et sans prise de tête, avec notre application web.&lt;/p&gt;

&lt;h2 id=&quot;pourquoi-des-applis-mobiles-et-pourquoi-maintenant&quot;&gt;Pourquoi des applis mobiles ? Et pourquoi maintenant ?&lt;/h2&gt;

&lt;p&gt;On a toujours voulu proposer la solution de partage de dépenses la plus accessible possible. Pendant des années, notre appli web a bien fait le travail : il suffisait d’un navigateur pour gérer facilement les dépenses de son groupe. Mais on a écouté vos retours, et on a bien compris que beaucoup d’entre vous préfèrent le confort d’une vraie appli mobile :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Accès immédiat&lt;/strong&gt; : à tes dépenses de groupe, sans ouvrir de navigateur&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Une utilisation plus fluide&lt;/strong&gt; : pensée spécialement pour le mobile&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025-09-10-mobile-app-release--app-kitty-overview.png&quot; alt=&quot;Tableau de bord de la Kitty sur iOS&quot; title=&quot;Tableau de bord de la Kitty sur iOS&quot; width=&quot;320&quot; /&gt; &lt;img src=&quot;/images/2025-09-10-mobile-app-release--app-entries.png&quot; alt=&quot;Dépenses de la Kitty sur iOS&quot; title=&quot;Dépenses de la Kitty sur iOS&quot; width=&quot;320&quot; /&gt;&lt;/p&gt;

&lt;!-- read-more --&gt;

&lt;h2 id=&quot;toujours-le-kittysplit-que-tu-connais&quot;&gt;Toujours le Kittysplit que tu connais&lt;/h2&gt;

&lt;p&gt;On est ravis d’arriver sur mobile, mais rassure-toi : Kittysplit ne change pas. On tient toujours à :&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;La simplicité&lt;/strong&gt; : pas de fonctions compliquées dont tu n’as pas besoin.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Pas d’inscription&lt;/strong&gt; : tu crées des Kitties sans créer de compte.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Un partage équitable&lt;/strong&gt; : plusieurs façons de répartir les dépenses de manière juste.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;La gratuité pour l’essentiel&lt;/strong&gt; : les fonctions de base restent gratuites.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Pas d’appli obligatoire&lt;/strong&gt; : les applis mobiles sont là pour rendre Kittysplit plus agréable sur téléphone, mais la version web continue de vivre.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025-09-10-mobile-app-release--kitties-on-vacation.svg&quot; alt=&quot;Des Kitties en vacances&quot; title=&quot;Des Kitties en vacances&quot; width=&quot;500&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;on-reste-fidèles-au-web&quot;&gt;On reste fidèles au web&lt;/h2&gt;

&lt;p&gt;On tient à te laisser le choix. On est très contents de pouvoir enfin proposer des applis mobiles, mais on veut être clairs : &lt;strong&gt;on continue à miser à fond sur la version web&lt;/strong&gt;. Tout le monde n’a pas envie de télécharger une appli de plus, et pour beaucoup d’entre vous, la souplesse d’une solution web reste précieuse.&lt;/p&gt;

&lt;p&gt;La version web de Kittysplit continuera de recevoir la même attention, les mêmes mises à jour et les mêmes nouveautés que nos applis mobiles.&lt;/p&gt;

&lt;h2 id=&quot;télécharge-la-dès-aujourdhui&quot;&gt;Télécharge-la dès aujourd’hui !&lt;/h2&gt;

&lt;p&gt;Nos nouvelles applis mobiles sont disponibles dès maintenant sur l’App Store d’Apple et sur Google Play. Que tu utilises Kittysplit depuis longtemps ou que tu viennes de nous découvrir, essaie l’appli, et garde la souplesse de la version web quand tu en as besoin.&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://apps.apple.com/app/apple-store/id6742316700?pt=127648574&amp;amp;ct=ks_blog_post_app_release&amp;amp;mt=8&quot;&gt;&lt;img src=&quot;/images/badge_apple_app_store.svg&quot; alt=&quot;Télécharger dans l&apos;App Store&quot; width=&quot;179&quot; /&gt;&lt;/a&gt; &lt;a href=&quot;https://play.google.com/store/apps/details?id=com.kittysplit&amp;amp;referrer=utm_campaign%3Dks_blog_link%26utm_medium%3Dks_blog%26utm_source%3Dks_blog_post_app_release&quot;&gt;&lt;img src=&quot;/images/badge_google_play_store.png&quot; alt=&quot;Disponible sur Google Play&quot; width=&quot;200&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;Ou continue à utiliser notre appli web sur &lt;a href=&quot;https://www.kittysplit.com/fr&quot;&gt;kittysplit.com&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;et-ensuite&quot;&gt;Et ensuite ?&lt;/h2&gt;
&lt;p&gt;Ce lancement sur mobile n’est qu’un début. On travaille déjà sur des améliorations et de nouvelles fonctionnalités, en partant de tous vos retours.&lt;/p&gt;

&lt;p&gt;On serait ravis d’avoir ton avis sur les nouvelles applis et tes idées pour la suite ! Comme toujours, notre but reste le même : rendre le partage des dépenses aussi simple et détendu que possible, maintenant sur encore plus d’appareils.&lt;/p&gt;
</description>
        <pubDate>Wed, 10 Sep 2025 00:00:00 +0000</pubDate>
        <link>https://blog.kittysplit.com/fr/application-mobile-kittysplit/</link>
        <guid isPermaLink="true">https://blog.kittysplit.com/fr/application-mobile-kittysplit/</guid>
      </item>
    
      <item>
        <title>Adsense a un sérieux problème</title>
        <description>&lt;p&gt;On avait déjà eu des soucis avec des pubs frauduleuses sur Adsense &lt;a href=&quot;/fr/adsense-et-les-pubs-malveillantes/&quot;&gt;il y a quelques années&lt;/a&gt;. Les arnaques un peu grossières, comme la fausse alerte virus de 2018, semblent avoir disparu, mais d’autres pubs frauduleuses ont fait leur apparition.&lt;/p&gt;

&lt;p&gt;L’an dernier, une personne nous a contactés pour résilier son abonnement. Sauf que Kittysplit ne propose aucun abonnement. On a vite compris qu’elle s’était fait arnaquer par une pub frauduleuse affichée pendant qu’elle utilisait Kittysplit sur son téléphone.&lt;/p&gt;

&lt;!-- read-more --&gt;

&lt;h2 id=&quot;comment-cette-personne-sest-fait-piéger&quot;&gt;Comment cette personne s’est fait piéger&lt;/h2&gt;

&lt;p&gt;L’arnaque est simple : la pub affiche un énorme bouton vert &lt;strong&gt;START&lt;/strong&gt; et se fait passer pour une partie de Kittysplit. Mais le bouton envoie vers un site tiers qui demande les coordonnées de carte bancaire. Le site fait croire que c’est pour payer un service de Kittysplit (par exemple pour régler les comptes avec tes amis, ou des frais d’utilisation du site). Au début, c’est gratuit ou presque, puis un abonnement mensuel commence à être prélevé, entre 20 et 60 €/$/£, pour un service que la personne n’a jamais voulu.&lt;/p&gt;

&lt;p&gt;Voici une capture d’écran d’une pub trompeuse :&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2024-03-adsense-scam.jpg&quot; alt=&quot;Exemple de pub trompeuse&quot; title=&quot;Exemple de pub trompeuse&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Dès qu’on a eu connaissance du problème, on a réagi rapidement et on a commencé à bloquer ces pubs dans le centre d’examen des annonces d’Adsense. Pendant un moment, ça a eu l’air de régler le problème, mais les pubs revenaient sans cesse sous d’autres formes, toujours avec un gros bouton bien voyant marqué &lt;strong&gt;START&lt;/strong&gt;, &lt;strong&gt;CONTINUE&lt;/strong&gt;, &lt;strong&gt;NEXT&lt;/strong&gt;, &lt;strong&gt;DOWNLOAD&lt;/strong&gt; ou quelque chose du genre.&lt;/p&gt;

&lt;p&gt;Il y a quelques mois, une autre personne qui s’était aussi fait arnaquer par ces pubs nous a contactés. Alors on a bloqué les pubs trompeuses encore et encore, on a passé des heures à bloquer des milliers de pubs et des centaines de comptes d’annonceurs, en espérant que ça règle le problème. Mais ça ne s’arrêtait pas, et on avait beau bloquer ces pubs, elles revenaient au bout de quelques jours.&lt;/p&gt;

&lt;h2 id=&quot;pourquoi-on-a-décidé-darrêter-les-pubs-pour-protéger-nos-utilisateurs&quot;&gt;Pourquoi on a décidé d’arrêter les pubs pour protéger nos utilisateurs&lt;/h2&gt;

&lt;p&gt;Mais il s’avère qu’il est impossible d’attraper toutes les mauvaises pubs, et récemment, une troisième personne s’est fait arnaquer. Alors on a décidé que ça suffisait et on a &lt;a href=&quot;https://x.com/kittysplit/status/1754481485736816847&quot;&gt;désactivé toutes les pubs le mois dernier&lt;/a&gt;. Kittysplit est donc désormais sans pub (youpi !), jusqu’à ce qu’on trouve une autre solution publicitaire ou qu’Adsense règle le problème. On sait bien que personne n’aime la pub, mais on compte sur les revenus publicitaires pour que les Kitties gratuites restent gratuites pour toi.&lt;/p&gt;

&lt;p&gt;Même si ça nous coûte de l’argent en revenus publicitaires perdus, protéger nos utilisateurs contre les arnaques des annonceurs malhonnêtes compte plus pour nous.&lt;/p&gt;

&lt;h2 id=&quot;mais-pourquoi-ça-arrive-encore&quot;&gt;Mais pourquoi ça arrive encore ?&lt;/h2&gt;

&lt;p&gt;La vraie question, c’est pourquoi Adsense laisse encore ces pubs revenir. Elles enfreignent clairement les règles d’Adsense (&lt;a href=&quot;https://support.google.com/adspolicy/answer/6020955&quot;&gt;Présentation trompeuse - Conception d’annonce trompeuse&lt;/a&gt;) : les pubs qui font qu’il est difficile pour l’utilisateur de comprendre qu’il interagit avec une pub sont interdites.&lt;/p&gt;

&lt;p&gt;Alors pourquoi Adsense continue-t-il à les diffuser ? Ces pubs devraient être assez simples à détecter et à bloquer. Enfin quoi, on est en 2024, vous êtes Google, vous avez de l’IA et tout le tralala. Que chaque propriétaire de site passe des heures et des heures à bloquer chaque pub une par une à chaque fois qu’elle revient au bout d’un jour ou deux, ce n’est clairement pas une solution viable. Google/Adsense doit régler ça de son côté !&lt;/p&gt;

&lt;p&gt;Google &lt;a href=&quot;https://blog.google/products/ads-commerce/google-ads-safety-report-2023/#enforcement&quot;&gt;affirme&lt;/a&gt; utiliser le machine learning et l’IA pour bloquer les pubs. Mais même l’IA la plus rudimentaire de 2024 devrait pouvoir repérer sans peine un gros bouton vert marqué « CONTINUE ». Et pourtant, les pubs reviennent sans arrêt.&lt;/p&gt;

&lt;p&gt;Qu’est-ce qui cloche, Adsense ?&lt;/p&gt;
</description>
        <pubDate>Thu, 28 Mar 2024 00:00:00 +0000</pubDate>
        <link>https://blog.kittysplit.com/fr/adsense-a-un-serieux-probleme/</link>
        <guid isPermaLink="true">https://blog.kittysplit.com/fr/adsense-a-un-serieux-probleme/</guid>
      </item>
    
  </channel>
</rss>
