<?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/nl/</link>
    <language>nl</language>
    <atom:link href="https://blog.kittysplit.com/nl/feed.xml" rel="self" type="application/rss+xml" />
    
      <item>
        <title>Maak kennis met accounts - je Kitties veilig en in sync</title>
        <description>&lt;p&gt;Accounts zijn er! Log in bij Kittysplit (web, iOS of Android) en al je Kitties gaan met je mee. Het is onze grootste update in tijden, en hij is helemaal optioneel.&lt;/p&gt;

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

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

&lt;h2 id=&quot;waarom-accounts&quot;&gt;Waarom accounts?&lt;/h2&gt;

&lt;p&gt;Kittysplit werkte lange tijd zonder accounts, en dat was een bewuste keuze. Geen account betekent: niet registreren, geen wachtwoorden onthouden en geen persoonsgegevens die we verzamelen. Toen hadden we ook &lt;a href=&quot;/webapp-vs-native-app&quot;&gt;nog geen mobiele apps&lt;/a&gt;, dus wisselen van apparaat was minder een probleem.&lt;/p&gt;

&lt;p&gt;Maar accounts stonden op nummer 1 in onze enquête, en sinds de &lt;a href=&quot;/nl/kittysplit-app-voor-android-en-ios/&quot;&gt;lancering van de apps&lt;/a&gt; vorig jaar hoorden we die vraag nog vaker. Dus eindelijk: accounts zijn er! Accounts bouwen is niet eens zo moeilijk. Ze optioneel houden, daar wordt het interessant. Het kostte flink wat denkwerk om ervoor te zorgen dat een Kitty precies hetzelfde werkt, of je nu ingelogd bent of niet.&lt;/p&gt;

&lt;h2 id=&quot;je-kitties-op-elk-apparaat&quot;&gt;Je Kitties op elk apparaat&lt;/h2&gt;

&lt;p&gt;Log in op het web of op je telefoon en je vindt overal dezelfde Kitties. Nooit meer oude chats doorspitten op zoek naar die ene link. Nieuwe telefoon? Andere browser? Gewoon inloggen en verder waar je gebleven was.&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;Inloggen of registreren&quot; title=&quot;Inloggen of registreren&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;Je profiel&quot; title=&quot;Je profiel&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;Inloggen of registreren&lt;/em&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;em&gt;Je profiel&lt;/em&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2 id=&quot;veiligere-kitties&quot;&gt;Veiligere Kitties&lt;/h2&gt;

&lt;p&gt;Maak je een Kitty aan terwijl je ingelogd bent, dan is hij van jou. Alleen jij kunt hem bewerken of verwijderen, dus er verdwijnt niet zomaar per ongeluk iets belangrijks. Je vrienden kunnen gewoon uitgaven toevoegen en verrekenen, net als altijd. Voor hen verandert er niets.&lt;/p&gt;

&lt;h2 id=&quot;je-vrienden-hebben-geen-account-nodig&quot;&gt;Je vrienden hebben geen account nodig&lt;/h2&gt;

&lt;p&gt;Een account is een keuze, en dat blijft ook zo. Nodig mensen uit zoals je altijd al deed: stuur ze de link en ze kunnen meteen beginnen. Niet registreren, geen gedoe. Daar draait Kittysplit nog steeds om.&lt;/p&gt;

&lt;h2 id=&quot;binnenkort-profielfotos&quot;&gt;Binnenkort: profielfoto’s&lt;/h2&gt;

&lt;p&gt;Profielfoto’s zijn als volgende aan de beurt: upload een leuke foto, dan ben je in de Kitties van je vrienden meer dan alleen een naam. En we hebben nog een paar ideeën speciaal voor accounts op de plank liggen.&lt;/p&gt;

&lt;p&gt;Veel deelplezier! 🐱✨&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;Feedback of ideeën? Laat het ons weten via &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; of stuur een mail naar 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/nl/maak-kennis-met-accounts/</link>
        <guid isPermaLink="true">https://blog.kittysplit.com/nl/maak-kennis-met-accounts/</guid>
      </item>
    
      <item>
        <title>Bijlagen bij uitgaven - zet foto&apos;s bij je uitgaven</title>
        <description>&lt;p&gt;Nieuw in Kittysplit: bijlagen bij uitgaven! Je kunt nu tot 3 afbeeldingen toevoegen aan elke uitgave in je Kitty. Dit is een &lt;strong&gt;Super Kitty&lt;/strong&gt;-functie, dus je moet je Kitty eerst opwaarderen.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2026-03-12/expense-attachments-1.png&quot; alt=&quot;Bijlagen bij uitgaven&quot; title=&quot;Bijlagen bij uitgaven&quot; width=&quot;320&quot; /&gt;&lt;/p&gt;

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

&lt;h2 id=&quot;wat-is-er-nieuw&quot;&gt;Wat is er nieuw?&lt;/h2&gt;

&lt;p&gt;Aan elke uitgave kun je nu foto’s hangen. Maak een foto van het bonnetje, de rekening van het restaurant of wat je ook maar deelt, en die blijft gewoon bij de uitgave staan, zodat iedereen hem kan zien.&lt;/p&gt;

&lt;p&gt;Je kunt tot 3 afbeeldingen per uitgave toevoegen, en daarmee red je het meestal wel. Denk aan bonnetjes, hotelrekeningen, concertkaartjes, of gewoon een foto van wat je gekocht hebt, zodat de groep weet waar ze aan meebetalen.&lt;/p&gt;

&lt;p&gt;Nooit meer weken later door je fotogalerij scrollen op zoek naar dat ene bonnetje. Voeg het toe zodra je de uitgave invoert en klaar.&lt;/p&gt;

&lt;h2 id=&quot;hoe-werkt-het&quot;&gt;Hoe werkt het?&lt;/h2&gt;

&lt;p&gt;Als je een uitgave toevoegt of bewerkt, zie je een optie om afbeeldingen toe te voegen. Kies er een uit je galerij. Wil je er later een vervangen? Verwijder hem gewoon en voeg een nieuwe toe.&lt;/p&gt;

&lt;h2 id=&quot;probeer-het-uit&quot;&gt;Probeer het uit&lt;/h2&gt;

&lt;p&gt;Waardeer je Kitty op naar een Super Kitty en zet een foto bij je volgende uitgave. Meer is het niet!&lt;/p&gt;

&lt;p&gt;Veel deelplezier! 🐱✨&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;Feedback of ideeën? Laat het ons weten via &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; of stuur een mail naar 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/nl/fotos-bij-je-uitgaven/</link>
        <guid isPermaLink="true">https://blog.kittysplit.com/nl/fotos-bij-je-uitgaven/</guid>
      </item>
    
      <item>
        <title>Nieuw: categorieën voor uitgaven - meer inzicht in je Kitties</title>
        <description>&lt;p&gt;We hebben iets nieuws voor Kittysplit: &lt;strong&gt;categorieën voor uitgaven&lt;/strong&gt;! Vanaf vandaag kun je al je uitgaven en inkomsten in je Kitties een categorie geven. Zo zie je een stuk beter waar je geld naartoe gaat.&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;Uitgave toevoegen&quot; title=&quot;Uitgave toevoegen&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;Filteren op categorie&quot; title=&quot;Filteren op categorie&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;Uitgave toevoegen&lt;/em&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;em&gt;Filteren op categorie&lt;/em&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

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

&lt;h2 id=&quot;wat-is-er-nieuw&quot;&gt;Wat is er nieuw?&lt;/h2&gt;

&lt;p&gt;Alles wat je aan je Kitty toevoegt, of het nu een uitgave is of inkomsten, kun je nu in een van 12 categorieën onderbrengen:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Huur en wonen&lt;/strong&gt; - Huur, hypotheek, woonverzekering en spullen voor het huishouden&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Accommodatie&lt;/strong&gt; - Hotels, vakantiehuizen en andere tijdelijke verblijven&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Eten en boodschappen&lt;/strong&gt; - De wekelijkse supermarktronde en eten voor in huis&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Uit eten en drinken&lt;/strong&gt; - Restaurants, bars, cafés en afhaal&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Vervoer&lt;/strong&gt; - Benzine, openbaar vervoer, taxi’s en huurauto’s&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Vaste lasten en communicatie&lt;/strong&gt; - Stroom, water, internet en telefoonrekeningen&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Gezondheid&lt;/strong&gt; - Medische kosten, apotheek en zorgverzekering&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Vrije tijd en activiteiten&lt;/strong&gt; - Uitjes, activiteiten, concerten en evenementen&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Winkelen en uitrusting&lt;/strong&gt; - Kleding, elektronica en andere aankopen&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Welzijn en verzorging&lt;/strong&gt; - Sportschool, sauna, beauty en zelfzorg&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Financiën en kosten&lt;/strong&gt; - Bankkosten, servicekosten en andere financiële kosten&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Overig&lt;/strong&gt; - Alles wat nergens anders in past&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Elke categorie heeft een eigen kleur, zodat je in één oogopslag ziet waar het geld heen gaat.&lt;/p&gt;

&lt;h2 id=&quot;filter-je-uitgaven&quot;&gt;Filter je uitgaven&lt;/h2&gt;

&lt;p&gt;Het eerste wat we met categorieën uitrollen, is &lt;strong&gt;filteren&lt;/strong&gt;. Je kunt je lijst met uitgaven nu op categorie filteren en zo snel terugvinden:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Alle uitgaven aan &lt;strong&gt;uit eten&lt;/strong&gt; tijdens jullie groepsreis&lt;/li&gt;
  &lt;li&gt;Wat jullie in je gedeelde huis aan &lt;strong&gt;boodschappen&lt;/strong&gt; uitgeven&lt;/li&gt;
  &lt;li&gt;De kosten voor &lt;strong&gt;vervoer&lt;/strong&gt; tijdens je roadtrip&lt;/li&gt;
  &lt;li&gt;Elke andere categorie die je wilt bekijken&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Zo heb je meteen antwoord op vragen als “Hoeveel hebben we aan eten uitgegeven?” of “Hoeveel waren we tijdens de vakantie kwijt aan accommodatie?”&lt;/p&gt;

&lt;h2 id=&quot;wat-komt-er-nog&quot;&gt;Wat komt er nog?&lt;/h2&gt;

&lt;p&gt;Dit is pas het begin. We werken al aan de volgende functies, zodat je nog meer hebt aan uitgaven met een categorie:&lt;/p&gt;

&lt;h3 id=&quot;maand-voor-maand-vergelijken&quot;&gt;Maand voor maand vergelijken&lt;/h3&gt;
&lt;p&gt;Zie hoe je uitgaven per categorie veranderen. Kruipt het boodschappenbedrag omhoog, of ga je juist minder vaak uit eten dan vorige maand?&lt;/p&gt;

&lt;h3 id=&quot;trends&quot;&gt;Trends&lt;/h3&gt;
&lt;p&gt;Ontdek patronen in je uitgaven over weken, maanden of zelfs jaren. Ideaal voor huisgenoten die hun gezamenlijke huishoudpot slimmer willen indelen.&lt;/p&gt;

&lt;h3 id=&quot;grafieken&quot;&gt;Grafieken&lt;/h3&gt;
&lt;p&gt;Overzichtelijke grafieken van je uitgaven per categorie. Taartdiagrammen voor de totale verdeling, staafdiagrammen om categorieën te vergelijken en een tijdlijn voor trends.&lt;/p&gt;

&lt;h2 id=&quot;waarom-categorieën-handig-zijn&quot;&gt;Waarom categorieën handig zijn&lt;/h2&gt;

&lt;p&gt;Of je nu een huis deelt met huisgenoten of een groepsvakantie plant, met categorieën:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Snap je je uitgaven beter&lt;/strong&gt; - Zie waar jullie gedeelde geld echt naartoe gaat&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Plan je slimmer&lt;/strong&gt; - Gebruik eerdere uitgaven als basis voor het budget van een volgend uitje&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Weet iedereen waar hij aan toe is&lt;/strong&gt; - Iedereen in de groep kan de verdeling per categorie zien&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;aan-de-slag&quot;&gt;Aan de slag&lt;/h2&gt;

&lt;p&gt;Categorieën zitten nu in de nieuwste versie van Kittysplit. Voeg een uitgave toe of bewerk er een, en je ziet meteen de optie om een categorie te kiezen. Zo simpel is het!&lt;/p&gt;

&lt;p&gt;Zoals altijd horen we graag wat je ervan vindt en welke ideeën je nog hebt.&lt;/p&gt;

&lt;p&gt;Veel deelplezier! 🐱✨&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;Ideeën om categorieën beter te laten werken voor jouw situatie? Laat het ons weten via &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; of stuur een mail naar 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/nl/categorieen-voor-uitgaven/</link>
        <guid isPermaLink="true">https://blog.kittysplit.com/nl/categorieen-voor-uitgaven/</guid>
      </item>
    
      <item>
        <title>Een beter overzicht en kattenavatars</title>
        <description>&lt;p&gt;Net op tijd voor kerst 2025 krijgen de Kittysplit-apps een update (v1.3), met twee verbeteringen die we al een tijdje op ons lijstje hadden staan:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Een nieuw overzicht&lt;/strong&gt; dat laat zien wat ertoe doet, zonder je te bedelven onder de cijfers&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Kattenavatars&lt;/strong&gt; voor een vriendelijkere, speelsere look&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;het-overzicht-voor-en-na&quot;&gt;Het overzicht: voor en na&lt;/h2&gt;

&lt;p&gt;Het overzicht is het eerste wat je ziet als je een Kitty opent. We hebben het zo omgebouwd dat je je belangrijkste vragen snel beantwoord ziet: wat kost het hele uitje in totaal? Wie moet wat betalen? Verder hebben we alles meer structuur gegeven met Material Design Cards. Op sommige plekken gaat dat zelfs verder dan alleen het overzicht.&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;Kitty-overzicht - voor&quot; title=&quot;Kitty-overzicht - voor&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;Kitty-overzicht - na&quot; title=&quot;Kitty-overzicht - na&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;Kitty-overzicht - voor&lt;/em&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;em&gt;Kitty-overzicht - na&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;Kitty-overzicht - na (donker)&quot; title=&quot;Kitty-overzicht - na (donker)&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;Beheer deelnemers - na (donker)&quot; title=&quot;Beheer deelnemers - na (donker)&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;Kitty-overzicht - na (donker)&lt;/em&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;em&gt;Beheer deelnemers - na (donker)&lt;/em&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2 id=&quot;een-kattenavatar-voor-elke-deelnemer&quot;&gt;Een kattenavatar voor elke deelnemer&lt;/h2&gt;

&lt;p&gt;In elke Kitty krijgt nu iedere deelnemer een eigen kattenavatar. Ja, dat past natuurlijk bij de naam Kittysplit, maar er zijn ook praktische redenen.&lt;/p&gt;

&lt;p&gt;Met een plaatje erbij pik je informatie sneller op dan met alleen tekst. Dankzij de katten:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;voelt de app minder als een spreadsheet&lt;/li&gt;
  &lt;li&gt;herken je deelnemers makkelijker in grotere groepen&lt;/li&gt;
  &lt;li&gt;zie je sneller wie wat betaald heeft als je door de uitgaven scrolt&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;Kattenavatars (voorbeeld)&quot; title=&quot;Kattenavatars (voorbeeld)&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;Kattenavatars (voorbeeld)&lt;/em&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2 id=&quot;wat-er-niet-verandert&quot;&gt;Wat er niet verandert&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Registreren is niet nodig&lt;/li&gt;
  &lt;li&gt;Werkt op web, iOS en Android&lt;/li&gt;
  &lt;li&gt;Slimme berekening van wie wie wat schuldig is&lt;/li&gt;
  &lt;li&gt;De meeste functies zijn gratis&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;de-update-binnenhalen&quot;&gt;De update binnenhalen&lt;/h2&gt;

&lt;p&gt;Op je telefoon krijg je de update via de App Store of Google Play.&lt;/p&gt;

&lt;p&gt;Laat ons weten wat je ervan vindt via info[at]kittysplit.com of de feedbackknop.&lt;/p&gt;
</description>
        <pubDate>Tue, 09 Dec 2025 00:00:00 +0000</pubDate>
        <link>https://blog.kittysplit.com/nl/verbeterd-overzicht-en-kattenavatars/</link>
        <guid isPermaLink="true">https://blog.kittysplit.com/nl/verbeterd-overzicht-en-kattenavatars/</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 gaat mobiel: onze apps voor Android en iOS zijn er</title>
        <description>&lt;p&gt;Na meer dan tien jaar alleen op het web is Kittysplit eindelijk ook als app voor Android en iOS te krijgen! Sinds onze lancering in 2012 helpen we vrienden, huisgenoten en reisgezelschappen om kosten eerlijk en zonder gedoe te delen met onze webapp.&lt;/p&gt;

&lt;h2 id=&quot;waarom-apps-en-waarom-nu&quot;&gt;Waarom apps? En waarom nu?&lt;/h2&gt;

&lt;p&gt;We wilden altijd al de makkelijkst bereikbare manier bieden om kosten te delen. Jarenlang deed onze webapp dat prima: iedereen met een browser kon er zonder moeite de groepsuitgaven mee bijhouden. Maar we hebben goed naar jullie feedback geluisterd, en veel van jullie hebben gewoon liever een echte app op je telefoon.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Direct toegang&lt;/strong&gt;: tot je groepsuitgaven, zonder eerst een browser te openen&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Prettiger in gebruik&lt;/strong&gt;: speciaal gemaakt voor je telefoon&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;Kitty-overzicht op iOS&quot; title=&quot;Kitty-overzicht op iOS&quot; width=&quot;320&quot; /&gt; &lt;img src=&quot;/images/2025-09-10-mobile-app-release--app-entries.png&quot; alt=&quot;Uitgaven in een Kitty op iOS&quot; title=&quot;Uitgaven in een Kitty op iOS&quot; width=&quot;320&quot; /&gt;&lt;/p&gt;

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

&lt;h2 id=&quot;nog-steeds-het-kittysplit-dat-je-kent&quot;&gt;Nog steeds het Kittysplit dat je kent&lt;/h2&gt;

&lt;p&gt;We zijn superblij met onze nieuwe apps, maar wees gerust: Kittysplit blijft gewoon Kittysplit. Waar we voor blijven gaan:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Eenvoud:&lt;/strong&gt; Geen ingewikkelde functies die je toch niet nodig hebt.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Registreren is niet nodig:&lt;/strong&gt; Maak Kitties aan zonder een account aan te maken.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Eerlijk verdelen:&lt;/strong&gt; Verschillende manieren om uitgaven eerlijk te verdelen.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Gratis basisgebruik:&lt;/strong&gt; De belangrijkste functies blijven gratis.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Geen app nodig:&lt;/strong&gt; De apps maken Kittysplit op je telefoon een stuk prettiger, maar Kittysplit op het web blijft gewoon bestaan.&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;Kitties op vakantie&quot; title=&quot;Kitties op vakantie&quot; width=&quot;500&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;we-blijven-het-web-trouw&quot;&gt;We blijven het web trouw&lt;/h2&gt;

&lt;p&gt;We vinden het belangrijk dat je zelf kunt kiezen. Hoe blij we ook zijn dat we eindelijk apps hebben, we willen het nog even benadrukken: &lt;strong&gt;we blijven volledig achter onze webversie staan&lt;/strong&gt;. Niet iedereen zit te wachten op nog een app, en voor veel mensen blijft de flexibiliteit van het web superhandig.&lt;/p&gt;

&lt;p&gt;De webversie van Kittysplit krijgt net zoveel aandacht, updates en nieuwe functies als onze apps.&lt;/p&gt;

&lt;h2 id=&quot;download-ze-vandaag-nog&quot;&gt;Download ze vandaag nog!&lt;/h2&gt;

&lt;p&gt;Onze nieuwe apps staan nu in de Apple App Store en de Google Play Store. Of je Kittysplit nu al jaren gebruikt of ons net ontdekt: probeer de apps eens uit. En het web blijft er natuurlijk ook gewoon voor je.&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;Download in de Apple 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;Ontdek het op 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;Of blijf onze webapp gebruiken op &lt;a href=&quot;https://www.kittysplit.com&quot;&gt;kittysplit.com&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;wat-komt-er-nog&quot;&gt;Wat komt er nog?&lt;/h2&gt;
&lt;p&gt;Deze lancering is pas het begin. We werken al aan verbeteringen en nieuwe functies, op basis van al jullie feedback.&lt;/p&gt;

&lt;p&gt;We horen graag wat je van de nieuwe apps vindt en welke ideeën je nog hebt! Zoals altijd willen we kosten delen zo simpel en stressvrij mogelijk maken, en dat nu op nog meer plekken.&lt;/p&gt;
</description>
        <pubDate>Wed, 10 Sep 2025 00:00:00 +0000</pubDate>
        <link>https://blog.kittysplit.com/nl/kittysplit-app-voor-android-en-ios/</link>
        <guid isPermaLink="true">https://blog.kittysplit.com/nl/kittysplit-app-voor-android-en-ios/</guid>
      </item>
    
      <item>
        <title>Adsense is stuk</title>
        <description>&lt;p&gt;We hadden &lt;a href=&quot;/nl/adsense-en-malafide-adverteerders/&quot;&gt;een paar jaar geleden&lt;/a&gt; al problemen met nep-advertenties via Adsense. Overduidelijke oplichterij zoals de nep-viruswaarschuwing uit 2018 lijkt verdwenen, maar er zijn andere oplichtersadvertenties voor in de plaats gekomen.&lt;/p&gt;

&lt;p&gt;Vorig jaar nam een gebruiker contact met ons op met de vraag of we zijn abonnement wilden opzeggen. Maar Kittysplit heeft helemaal geen abonnementen. Al snel ontdekten we dat de gebruiker was opgelicht door een frauduleuze advertentie die verscheen terwijl hij Kittysplit op zijn telefoon gebruikte.&lt;/p&gt;

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

&lt;h2 id=&quot;hoe-de-gebruiker-werd-misleid&quot;&gt;Hoe de gebruiker werd misleid&lt;/h2&gt;

&lt;p&gt;De truc is simpel: de advertentie toont een enorme groene knop met &lt;strong&gt;START&lt;/strong&gt; en doet alsof die bij Kittysplit hoort. Maar de knop stuurt je naar een site van een derde partij die om je creditcardgegevens vraagt. Die site doet alsof de kosten voor een dienst van Kittysplit zijn (bijvoorbeeld verrekenen met je vrienden, of kosten om de site te gebruiken). In het begin is het gratis of bijna gratis, maar daarna wordt er maandelijks een abonnement afgeschreven van ergens tussen de 20 en 60 €/$/£, voor een dienst waar de gebruiker nooit om heeft gevraagd.&lt;/p&gt;

&lt;p&gt;Hier is een screenshot van zo’n misleidende advertentie:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2024-03-adsense-scam.jpg&quot; alt=&quot;Voorbeeld van een misleidende advertentie&quot; title=&quot;Voorbeeld van een misleidende advertentie&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Toen we het probleem ontdekten, kwamen we snel in actie en begonnen we zulke advertenties te blokkeren in het advertentiecontrolecentrum van Adsense. Dat leek het probleem een tijdje op te lossen, maar de advertenties bleven terugkomen in andere varianten, altijd met een grote, felle knop met &lt;strong&gt;START&lt;/strong&gt;, &lt;strong&gt;DOORGAAN&lt;/strong&gt;, &lt;strong&gt;VOLGENDE&lt;/strong&gt;, &lt;strong&gt;DOWNLOADEN&lt;/strong&gt; of iets vergelijkbaars.&lt;/p&gt;

&lt;p&gt;Een paar maanden geleden nam er weer een gebruiker contact met ons op die ook door deze advertenties was opgelicht. Dus blokkeerden we de misleidende advertenties opnieuw, en opnieuw. We waren uren bezig met het blokkeren van duizenden advertenties en honderden adverteerdersaccounts, in de hoop dat het probleem dan opgelost zou zijn. Maar het hield gewoon niet op: hoe vaak we die advertenties ook blokkeerden, na een paar dagen waren ze er weer.&lt;/p&gt;

&lt;h2 id=&quot;waarom-we-zijn-gestopt-met-advertenties-om-onze-gebruikers-te-beschermen&quot;&gt;Waarom we zijn gestopt met advertenties om onze gebruikers te beschermen&lt;/h2&gt;

&lt;p&gt;Het blijkt gewoon onmogelijk om alle slechte advertenties tegen te houden, en onlangs is er een derde gebruiker opgelicht. Toen vonden we het genoeg en hebben we &lt;a href=&quot;https://x.com/kittysplit/status/1754481485736816847&quot;&gt;vorige maand alle advertenties uitgezet&lt;/a&gt;. Kittysplit is nu dus reclamevrij (joepie!), totdat we een andere advertentieoplossing vinden of Adsense het probleem oplost. We weten dat niemand van advertenties houdt, maar we hebben die inkomsten wel nodig om gratis Kitties gratis te houden.&lt;/p&gt;

&lt;p&gt;Het kost ons geld aan gemiste advertentie-inkomsten, maar we vinden het belangrijker om onze gebruikers te beschermen tegen oplichting door malafide adverteerders.&lt;/p&gt;

&lt;h2 id=&quot;maar-waarom-gebeurt-het-nog-steeds&quot;&gt;Maar waarom gebeurt het nog steeds?&lt;/h2&gt;

&lt;p&gt;De echte vraag is waarom Adsense deze advertenties steeds weer toelaat. Ze gaan duidelijk in tegen het beleid van Adsense (&lt;a href=&quot;https://support.google.com/adspolicy/answer/6020955&quot;&gt;Misrepresentation - Misleading ad design&lt;/a&gt;): advertenties die het de gebruiker moeilijk maken om te zien dat hij met een advertentie te maken heeft, zijn niet toegestaan.&lt;/p&gt;

&lt;p&gt;Waarom blijft Adsense ze dan toch tonen? Zulke advertenties zouden vrij makkelijk te herkennen en te blokkeren moeten zijn. Kom op, het is 2024, jullie zijn Google en hebben AI en zo. Dat elke site-eigenaar eindeloos veel uren kwijt is aan het één voor één blokkeren van advertenties die na een dag of twee gewoon terugkomen, is duidelijk geen werkbare oplossing. Google/Adsense moet dit aan zijn kant oplossen!&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;beweert&lt;/a&gt; machine learning en AI te gebruiken om advertenties te blokkeren. Maar zelfs de simpelste AI van 2024 zou toch makkelijk een dikke groene knop met “DOORGAAN” moeten kunnen herkennen. Toch blijven de advertenties terugkomen.&lt;/p&gt;

&lt;p&gt;Wat is er aan de hand, Adsense?&lt;/p&gt;
</description>
        <pubDate>Thu, 28 Mar 2024 00:00:00 +0000</pubDate>
        <link>https://blog.kittysplit.com/nl/adsense-is-stuk/</link>
        <guid isPermaLink="true">https://blog.kittysplit.com/nl/adsense-is-stuk/</guid>
      </item>
    
  </channel>
</rss>
