<?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/de/</link>
    <language>de</language>
    <atom:link href="https://blog.kittysplit.com/de/feed.xml" rel="self" type="application/rss+xml" />
    
      <item>
        <title>Kittysplit-Konten: Deine Kitties, sicher und synchron</title>
        <description>&lt;p&gt;Ab sofort gibt es Konten bei Kittysplit. Melde dich an (im Web, auf iOS oder Android) und all deine Kitties sind mit dabei. Es ist unser größtes Update seit Langem, und es ist komplett freiwillig.&lt;/p&gt;

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

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

&lt;h2 id=&quot;warum-konten&quot;&gt;Warum Konten?&lt;/h2&gt;

&lt;p&gt;Lange Zeit kam Kittysplit ganz ohne Konten aus, und das war Absicht. Kein Konto heißt: keine Registrierung, keine Passwörter, die man sich merken muss, keine persönlichen Daten, die gesammelt werden. Damals hatten wir auch noch &lt;a href=&quot;/webapp-vs-native-app&quot;&gt;keine Apps fürs Handy&lt;/a&gt;, also war der Wechsel zwischen Geräten kein großes Thema.&lt;/p&gt;

&lt;p&gt;Aber Konten standen in unserer Umfrage ganz oben auf der Wunschliste, und seit dem &lt;a href=&quot;/de/kittysplit-app-fuer-android-und-ios/&quot;&gt;Start unserer Apps&lt;/a&gt; letztes Jahr haben wir den Wunsch noch öfter gehört. Jetzt sind sie endlich da! Konten an sich einzubauen ist gar nicht so schwer. Spannend wird es, wenn sie freiwillig bleiben sollen. Wir haben viel Planung reingesteckt, damit ein Kitty genau gleich funktioniert, egal ob du angemeldet bist oder nicht.&lt;/p&gt;

&lt;h2 id=&quot;deine-kitties-auf-jedem-gerät&quot;&gt;Deine Kitties, auf jedem Gerät&lt;/h2&gt;

&lt;p&gt;Melde dich im Browser oder auf dem Handy an und du findest überall dieselben Kitties. Kein Durchwühlen alter Chats mehr, nur um den Link zu finden. Neues Handy? Anderer Browser? Einfach anmelden und da weitermachen, wo du aufgehört hast.&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;Anmelden oder registrieren&quot; title=&quot;Anmelden oder registrieren&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;Dein Profil&quot; title=&quot;Dein Profil&quot; width=&quot;320&quot; /&gt;&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;em&gt;Anmelden oder registrieren&lt;/em&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;em&gt;Dein Profil&lt;/em&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2 id=&quot;mehr-sicherheit-für-deine-kitties&quot;&gt;Mehr Sicherheit für deine Kitties&lt;/h2&gt;

&lt;p&gt;Wenn du ein Kitty erstellst, während du angemeldet bist, gehört es dir. Nur du kannst es bearbeiten oder löschen, so geht nichts Wichtiges aus Versehen verloren. Deine Freunde können trotzdem wie gewohnt Ausgaben eintragen und ihre Schulden begleichen. Für sie ändert sich nichts.&lt;/p&gt;

&lt;h2 id=&quot;deine-freunde-brauchen-kein-konto&quot;&gt;Deine Freunde brauchen kein Konto&lt;/h2&gt;

&lt;p&gt;Ein Konto ist freiwillig, und das bleibt auch so. Lade Leute ein wie bisher: Schick ihnen den Link und sie können direkt loslegen. Keine Registrierung, kein Aufwand. Genau das macht Kittysplit nach wie vor aus.&lt;/p&gt;

&lt;h2 id=&quot;demnächst-profilbilder&quot;&gt;Demnächst: Profilbilder&lt;/h2&gt;

&lt;p&gt;Als Nächstes kommen Profilbilder: Lade ein schönes Foto hoch, dann bist du in den Kitties deiner Freunde mehr als nur ein Name. Und wir haben noch ein paar weitere Ideen, die es nur mit Konto geben wird.&lt;/p&gt;

&lt;p&gt;Viel Spaß beim Kostenteilen! 🐱✨&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;Feedback oder Ideen? Schreib uns auf &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; oder per E-Mail an 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/de/kittysplit-konten/</link>
        <guid isPermaLink="true">https://blog.kittysplit.com/de/kittysplit-konten/</guid>
      </item>
    
      <item>
        <title>Anhänge für Ausgaben: Fotos direkt zur Ausgabe speichern</title>
        <description>&lt;p&gt;Neu bei Kittysplit: Anhänge für Ausgaben! Ab sofort kannst du an jede Ausgabe in deinem Kitty bis zu 3 Bilder hängen. Das ist eine &lt;strong&gt;Super Kitty&lt;/strong&gt;-Funktion, du musst dein Kitty also upgraden, um sie zu nutzen.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2026-03-12/expense-attachments-1.png&quot; alt=&quot;Anhänge für Ausgaben&quot; title=&quot;Anhänge für Ausgaben&quot; width=&quot;320&quot; /&gt;&lt;/p&gt;

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

&lt;h2 id=&quot;was-ist-neu&quot;&gt;Was ist neu?&lt;/h2&gt;

&lt;p&gt;Jede Ausgabe kann jetzt Fotos haben. Mach ein Bild vom Kassenzettel, von der Restaurantrechnung oder von dem, was ihr gerade teilt, und es bleibt direkt bei der Ausgabe, für alle sichtbar.&lt;/p&gt;

&lt;p&gt;Pro Ausgabe gehen bis zu 3 Bilder, das sollte für die meisten Fälle reichen. Zum Beispiel Kassenbons, Hotelrechnungen, Konzerttickets oder einfach ein Foto von dem, was du gekauft hast, damit alle wissen, wofür sie mitbezahlen.&lt;/p&gt;

&lt;p&gt;Kein wochenlanges Suchen mehr in der Fotogalerie nach diesem einen Kassenbon. Häng ihn gleich beim Eintragen der Ausgabe an und fertig.&lt;/p&gt;

&lt;h2 id=&quot;so-funktionierts&quot;&gt;So funktioniert’s&lt;/h2&gt;

&lt;p&gt;Wenn du eine Ausgabe hinzufügst oder bearbeitest, kannst du Bilder anhängen. Such dir einfach eins aus deiner Galerie aus. Willst du später ein Bild austauschen, entfernst du es und fügst ein neues hinzu.&lt;/p&gt;

&lt;h2 id=&quot;probiers-aus&quot;&gt;Probier’s aus&lt;/h2&gt;

&lt;p&gt;Upgrade dein Kitty zum Super Kitty und häng an deine nächste Ausgabe ein Foto. Das war’s schon!&lt;/p&gt;

&lt;p&gt;Viel Spaß beim Kostenteilen! 🐱✨&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;Feedback oder Ideen? Schreib uns auf &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; oder per E-Mail an 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/de/anhaenge-fuer-ausgaben/</link>
        <guid isPermaLink="true">https://blog.kittysplit.com/de/anhaenge-fuer-ausgaben/</guid>
      </item>
    
      <item>
        <title>Neu: Kategorien für Ausgaben - mehr Überblick in deinen Kitties</title>
        <description>&lt;p&gt;Wir haben eine neue Funktion für Kittysplit, auf die wir uns richtig freuen: &lt;strong&gt;Kategorien für Ausgaben&lt;/strong&gt;! Ab heute kannst du jede Ausgabe und Einnahme in deinen Kitties einer Kategorie zuordnen. So siehst du viel besser, wo euer Geld eigentlich hingeht.&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;Ausgabe hinzufügen&quot; title=&quot;Ausgabe hinzufügen&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;Nach Kategorie filtern&quot; title=&quot;Nach Kategorie filtern&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;Ausgabe hinzufügen&lt;/em&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;em&gt;Nach Kategorie filtern&lt;/em&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

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

&lt;h2 id=&quot;was-ist-neu&quot;&gt;Was ist neu?&lt;/h2&gt;

&lt;p&gt;Jede Ausgabe oder Einnahme in deinem Kitty kann jetzt einer von 12 Kategorien zugeordnet werden:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Haus &amp;amp; Miete&lt;/strong&gt; - Miete, Kreditrate, Hausratversicherung und alles für den Haushalt&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Unterkunft&lt;/strong&gt; - Hotels, Ferienwohnungen und andere Unterkünfte auf Zeit&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Lebensmittel &amp;amp; Einkäufe&lt;/strong&gt; - Der Wocheneinkauf im Supermarkt und Vorräte für zu Hause&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Restaurant &amp;amp; Ausgehen&lt;/strong&gt; - Restaurants, Bars, Cafés und Essen zum Mitnehmen&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Mobilität&lt;/strong&gt; - Sprit, Bus und Bahn, Fahrdienste und Mietwagen&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Nebenkosten &amp;amp; Kommunikation&lt;/strong&gt; - Strom, Wasser, Internet und Handyrechnung&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Gesundheit&lt;/strong&gt; - Arztkosten, Apotheke und Krankenversicherung&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Hobby &amp;amp; Aktivitäten&lt;/strong&gt; - Freizeit, Ausflüge, Konzerte und Events&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Shopping &amp;amp; Ausrüstung&lt;/strong&gt; - Kleidung, Elektronik und sonstige Einkäufe&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Wellness &amp;amp; Körperpflege&lt;/strong&gt; - Fitnessstudio, Spa, Kosmetik und alles, was dir guttut&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Finanzen &amp;amp; Gebühren&lt;/strong&gt; - Bankgebühren, Servicegebühren und andere Finanzkosten&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Verschiedenes&lt;/strong&gt; - Alles, was sonst nirgends reinpasst&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Jede Kategorie hat ihre eigene Farbe, so erkennst du Muster auf einen Blick.&lt;/p&gt;

&lt;h2 id=&quot;ausgaben-filtern&quot;&gt;Ausgaben filtern&lt;/h2&gt;

&lt;p&gt;Als Erstes kannst du mit den Kategorien deine &lt;strong&gt;Ausgaben filtern&lt;/strong&gt;. Du kannst deine Ausgabenliste jetzt nach Kategorie filtern und findest damit schnell:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Alle &lt;strong&gt;Restaurantbesuche&lt;/strong&gt; auf eurer Gruppenreise&lt;/li&gt;
  &lt;li&gt;Was ihr in der WG für &lt;strong&gt;Lebensmittel&lt;/strong&gt; ausgegeben habt&lt;/li&gt;
  &lt;li&gt;Die &lt;strong&gt;Fahrtkosten&lt;/strong&gt; für euren Roadtrip&lt;/li&gt;
  &lt;li&gt;Jede andere Kategorie, die du dir genauer anschauen willst&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Damit sind Fragen wie “Wie viel haben wir eigentlich fürs Essen ausgegeben?” oder “Was hat die Unterkunft im Urlaub gekostet?” ganz schnell beantwortet.&lt;/p&gt;

&lt;h2 id=&quot;wie-gehts-weiter&quot;&gt;Wie geht’s weiter?&lt;/h2&gt;

&lt;p&gt;Das ist erst der Anfang. Wir arbeiten schon an den nächsten Funktionen, damit du noch mehr aus deinen Kategorien herausholen kannst:&lt;/p&gt;

&lt;h3 id=&quot;monatsvergleich&quot;&gt;Monatsvergleich&lt;/h3&gt;
&lt;p&gt;Sieh, wie sich deine Ausgaben pro Kategorie über die Zeit verändern. Wird der Einkauf jeden Monat ein bisschen teurer, oder warst du diesen Monat seltener essen als im letzten?&lt;/p&gt;

&lt;h3 id=&quot;trends&quot;&gt;Trends&lt;/h3&gt;
&lt;p&gt;Erkenne Muster und Trends über Wochen, Monate oder sogar Jahre. Ideal für WGs, die ihr gemeinsames Haushaltsbudget im Griff behalten wollen.&lt;/p&gt;

&lt;h3 id=&quot;diagramme&quot;&gt;Diagramme&lt;/h3&gt;
&lt;p&gt;Übersichtliche Diagramme, die zeigen, wie sich eure Ausgaben auf die Kategorien verteilen: Tortendiagramme für die Gesamtverteilung, Balkendiagramme zum Vergleichen der Kategorien und Zeitverläufe für die Entwicklung eurer Ausgaben.&lt;/p&gt;

&lt;h2 id=&quot;warum-kategorien&quot;&gt;Warum Kategorien?&lt;/h2&gt;

&lt;p&gt;Egal ob du dir mit anderen eine Wohnung teilst oder einen Gruppenurlaub planst, Kategorien helfen dir:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Ausgaben besser zu verstehen&lt;/strong&gt; - Sieh, wo euer gemeinsames Geld wirklich hingeht&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Besser zu planen&lt;/strong&gt; - Nutze die Zahlen vom letzten Mal fürs Budget beim nächsten Trip&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Für Transparenz zu sorgen&lt;/strong&gt; - Alle in der Gruppe sehen, wie sich die Ausgaben auf die Kategorien verteilen&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;gleich-ausprobieren&quot;&gt;Gleich ausprobieren&lt;/h2&gt;

&lt;p&gt;Kategorien gibt es ab sofort in der neuesten Version von Kittysplit. Füge einfach eine Ausgabe hinzu oder bearbeite eine bestehende, dann kannst du eine Kategorie auswählen. So einfach ist das!&lt;/p&gt;

&lt;p&gt;Wie immer freuen wir uns über dein Feedback und deine Ideen, was wir noch besser machen können.&lt;/p&gt;

&lt;p&gt;Viel Spaß beim Kostenteilen! 🐱✨&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;Du hast eine Idee, wie die Kategorien für dich noch besser funktionieren würden? Schreib uns auf &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; oder per E-Mail an 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/de/kategorien-fuer-ausgaben/</link>
        <guid isPermaLink="true">https://blog.kittysplit.com/de/kategorien-fuer-ausgaben/</guid>
      </item>
    
      <item>
        <title>Neue Übersicht und Katzen-Avatare</title>
        <description>&lt;p&gt;Pünktlich zu Weihnachten 2025 gibt es ein Update für die Kittysplit-Apps (v1.3). Es dreht sich um zwei Dinge, die wir schon länger angehen wollten:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Eine neu gestaltete Übersicht&lt;/strong&gt;, die zeigt, was wichtig ist, ohne dich mit Zahlen zu erschlagen&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Katzen-Avatare&lt;/strong&gt; für einen freundlicheren, verspielteren Look&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;die-übersicht-vorher-und-nachher&quot;&gt;Die Übersicht: vorher und nachher&lt;/h2&gt;

&lt;p&gt;Die Übersicht ist das Erste, was du siehst, wenn du ein Kitty öffnest. Wir haben sie so überarbeitet, dass sie deine wichtigsten Fragen schnell beantwortet: Was hat das Ganze insgesamt gekostet? Wer schuldet wem was? Insgesamt haben wir versucht, mit Karten im Material Design mehr Struktur reinzubringen. Das geht an manchen Stellen sogar über die Übersicht hinaus.&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-Übersicht vorher&quot; title=&quot;Kitty-Übersicht vorher&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-Übersicht nachher&quot; title=&quot;Kitty-Übersicht nachher&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-Übersicht vorher&lt;/em&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;em&gt;Kitty-Übersicht nachher&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-Übersicht nachher (dunkel)&quot; title=&quot;Kitty-Übersicht nachher (dunkel)&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;Teilnehmer bearbeiten nachher (dunkel)&quot; title=&quot;Teilnehmer bearbeiten nachher (dunkel)&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-Übersicht nachher (dunkel)&lt;/em&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;em&gt;Teilnehmer bearbeiten nachher (dunkel)&lt;/em&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2 id=&quot;ein-katzen-avatar-für-alle-teilnehmer&quot;&gt;Ein Katzen-Avatar für alle Teilnehmer&lt;/h2&gt;

&lt;p&gt;In jedem Kitty bekommen jetzt alle Teilnehmer ihren eigenen Katzen-Avatar. Klar, das passt zum Namen Kittysplit, aber es gibt auch ganz praktische Gründe.&lt;/p&gt;

&lt;p&gt;Bilder erfasst man schneller als reinen Text. Mit den Katzen:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;fühlt sich die App weniger nach Tabelle an&lt;/li&gt;
  &lt;li&gt;erkennst du die Teilnehmer auch in größeren Gruppen schneller&lt;/li&gt;
  &lt;li&gt;siehst du beim Scrollen durch die Ausgaben sofort, wer was bezahlt hat&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;Katzen-Avatare (Beispiel)&quot; title=&quot;Katzen-Avatare (Beispiel)&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;Katzen-Avatare (Beispiel)&lt;/em&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2 id=&quot;was-gleich-bleibt&quot;&gt;Was gleich bleibt&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Keine Registrierung nötig&lt;/li&gt;
  &lt;li&gt;Läuft im Web, auf iOS und auf Android&lt;/li&gt;
  &lt;li&gt;Clevere Berechnung, wer wem was schuldet&lt;/li&gt;
  &lt;li&gt;Die meisten Funktionen sind kostenlos&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;hol-dir-das-update&quot;&gt;Hol dir das Update&lt;/h2&gt;

&lt;p&gt;Wenn du die App nutzt, bekommst du das Update über den App Store oder Google Play.&lt;/p&gt;

&lt;p&gt;Sag uns, wie du es findest: per E-Mail an info[at]kittysplit.com oder über den Feedback-Button.&lt;/p&gt;
</description>
        <pubDate>Tue, 09 Dec 2025 00:00:00 +0000</pubDate>
        <link>https://blog.kittysplit.com/de/neue-uebersicht-und-katzen-avatare/</link>
        <guid isPermaLink="true">https://blog.kittysplit.com/de/neue-uebersicht-und-katzen-avatare/</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 wird mobil: Unsere Apps für Android und iOS sind da</title>
        <description>&lt;p&gt;Nach über zehn Jahren als reine Web-App gibt es Kittysplit jetzt endlich auch als App für Android und iOS! Seit unserem Start 2012 helfen wir Freunden, WGs und Reisegruppen dabei, ihre Ausgaben fair und ohne Stress aufzuteilen, bisher eben über den Browser.&lt;/p&gt;

&lt;h2 id=&quot;warum-apps-und-warum-jetzt&quot;&gt;Warum Apps? Und warum jetzt?&lt;/h2&gt;

&lt;p&gt;Uns war immer wichtig, dass jeder Kittysplit ganz einfach nutzen kann. Unsere Web-App hat das jahrelang gut gemacht: Wer einen Browser hat, kann damit die Ausgaben seiner Gruppe verwalten. Aber wir haben auf euer Feedback gehört, und viele von euch hätten lieber eine richtige App auf dem Handy.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Schneller Zugriff&lt;/strong&gt;: auf eure gemeinsamen Ausgaben, ohne erst den Browser zu öffnen&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Angenehmer zu bedienen&lt;/strong&gt;: speziell fürs Handy gemacht&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-Übersicht auf iOS&quot; title=&quot;Kitty-Übersicht auf iOS&quot; width=&quot;320&quot; /&gt; &lt;img src=&quot;/images/2025-09-10-mobile-app-release--app-entries.png&quot; alt=&quot;Kitty-Einträge auf iOS&quot; title=&quot;Kitty-Einträge auf iOS&quot; width=&quot;320&quot; /&gt;&lt;/p&gt;

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

&lt;h2 id=&quot;immer-noch-das-kittysplit-das-du-kennst&quot;&gt;Immer noch das Kittysplit, das du kennst&lt;/h2&gt;

&lt;p&gt;Wir freuen uns riesig über die Apps, aber keine Sorge: An Kittysplit selbst ändert sich nichts. Dabei bleibt es:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Einfachheit:&lt;/strong&gt; Keine komplizierten Funktionen, die du gar nicht brauchst.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Keine Registrierung:&lt;/strong&gt; Du kannst Kitties erstellen, ohne ein Konto anzulegen.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Faires Aufteilen:&lt;/strong&gt; Verschiedene Möglichkeiten, Ausgaben gerecht aufzuteilen.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Kostenlos im Kern:&lt;/strong&gt; Die Grundfunktionen bleiben gratis.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Keine App nötig:&lt;/strong&gt; Die Apps sind ein Angebot für alle, die Kittysplit am Handy noch bequemer nutzen wollen. Kittysplit im Web bleibt.&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 im Urlaub&quot; title=&quot;Kitties im Urlaub&quot; width=&quot;500&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;wir-bleiben-im-web&quot;&gt;Wir bleiben im Web&lt;/h2&gt;

&lt;p&gt;Wir finden, ihr solltet die Wahl haben. So sehr wir uns freuen, endlich Apps anbieten zu können: &lt;strong&gt;Die Web-Version bleibt uns genauso wichtig.&lt;/strong&gt; Nicht jeder hat Lust, noch eine App zu installieren, und dass Kittysplit einfach im Browser läuft, ist für viele von euch ein großer Vorteil.&lt;/p&gt;

&lt;p&gt;Die Web-Version von Kittysplit bekommt weiterhin genauso viel Aufmerksamkeit, Updates und neue Funktionen wie die Apps.&lt;/p&gt;

&lt;h2 id=&quot;jetzt-herunterladen&quot;&gt;Jetzt herunterladen!&lt;/h2&gt;

&lt;p&gt;Die neuen Apps gibt es ab sofort im App Store und bei Google Play. Egal ob du Kittysplit schon lange nutzt oder uns gerade erst entdeckst: Probier die App aus, das Web bleibt dir natürlich trotzdem erhalten.&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;Laden im 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;Jetzt bei 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;Oder nutz einfach weiter unsere Web-App auf &lt;a href=&quot;https://www.kittysplit.com&quot;&gt;kittysplit.com&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;wie-gehts-weiter&quot;&gt;Wie geht’s weiter?&lt;/h2&gt;
&lt;p&gt;Die Apps sind erst der Anfang. Wir arbeiten schon an Verbesserungen und neuen Funktionen, die auf eurem Feedback beruhen.&lt;/p&gt;

&lt;p&gt;Wir sind gespannt, wie euch die neuen Apps gefallen, und freuen uns über alle Ideen, was wir noch besser machen können! Unser Ziel bleibt dasselbe: Kosten teilen so einfach und stressfrei wie möglich machen, jetzt eben auf noch mehr Geräten.&lt;/p&gt;
</description>
        <pubDate>Wed, 10 Sep 2025 00:00:00 +0000</pubDate>
        <link>https://blog.kittysplit.com/de/kittysplit-app-fuer-android-und-ios/</link>
        <guid isPermaLink="true">https://blog.kittysplit.com/de/kittysplit-app-fuer-android-und-ios/</guid>
      </item>
    
      <item>
        <title>Adsense ist kaputt</title>
        <description>&lt;p&gt;Mit betrügerischen Anzeigen bei Adsense hatten wir schon &lt;a href=&quot;/de/adsense-und-schwarze-schafe&quot;&gt;vor einigen Jahren&lt;/a&gt; Ärger. Ziemlich offensichtliche Maschen wie die falsche Viruswarnung von 2018 scheinen zwar verschwunden zu sein, dafür sind andere Betrugsanzeigen aufgetaucht.&lt;/p&gt;

&lt;p&gt;Letztes Jahr hat sich ein Nutzer bei uns gemeldet, weil er sein Abo kündigen wollte. Nur: Kittysplit bietet gar keine Abos an. Schnell war klar, dass er auf eine betrügerische Anzeige hereingefallen war, die ihm beim Benutzen von Kittysplit auf dem Handy angezeigt wurde.&lt;/p&gt;

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

&lt;h2 id=&quot;wie-der-nutzer-reingelegt-wurde&quot;&gt;Wie der Nutzer reingelegt wurde&lt;/h2&gt;

&lt;p&gt;Die Masche ist simpel: Die Anzeige zeigt einen riesigen grünen &lt;strong&gt;START&lt;/strong&gt;-Button und tut so, als gehöre sie zu Kittysplit. Der Button führt aber auf die Seite eines Drittanbieters, die nach den Kreditkartendaten fragt. Dort wird so getan, als wären die Kosten für einen Dienst von Kittysplit (z. B. fürs Schuldenbegleichen mit deinen Freunden oder für die Nutzung der Seite). Anfangs ist es kostenlos oder fast kostenlos, dann wird aber ein monatliches Abo zwischen 20 und 60 €/$/£ fällig, für einen Dienst, den der Nutzer nie haben wollte.&lt;/p&gt;

&lt;p&gt;Hier ein Screenshot einer irreführenden Anzeige:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2024-03-adsense-scam.jpg&quot; alt=&quot;Beispiel für eine irreführende Anzeige&quot; title=&quot;Beispiel für eine irreführende Anzeige&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Als wir von dem Problem erfahren haben, haben wir schnell reagiert und solche Anzeigen im Adsense-Prüfcenter für Anzeigen blockiert. Eine Zeit lang schien das Problem damit gelöst, aber die Anzeigen kamen in immer neuen Varianten zurück: immer ein großer, knalliger Button mit &lt;strong&gt;START&lt;/strong&gt;, &lt;strong&gt;WEITER&lt;/strong&gt;, &lt;strong&gt;NÄCHSTER SCHRITT&lt;/strong&gt;, &lt;strong&gt;DOWNLOAD&lt;/strong&gt; oder so ähnlich.&lt;/p&gt;

&lt;p&gt;Vor ein paar Monaten hat sich dann noch ein Nutzer gemeldet, der ebenfalls auf diese Anzeigen hereingefallen war. Also haben wir die irreführenden Anzeigen wieder und wieder blockiert. Wir haben viele Stunden damit verbracht, Tausende Anzeigen und Hunderte Werbekonten zu sperren, in der Hoffnung, dass das Problem damit gelöst ist. Aber es hörte einfach nicht auf, und egal wie oft wir diese Anzeigen blockiert haben, nach ein paar Tagen waren sie wieder da.&lt;/p&gt;

&lt;h2 id=&quot;warum-wir-keine-werbung-mehr-zeigen-um-unsere-nutzer-zu-schützen&quot;&gt;Warum wir keine Werbung mehr zeigen, um unsere Nutzer zu schützen&lt;/h2&gt;

&lt;p&gt;Es hat sich aber herausgestellt, dass man unmöglich alle schlechten Anzeigen erwischen kann, und vor Kurzem ist ein dritter Nutzer betrogen worden. Da war für uns das Maß voll, und wir haben &lt;a href=&quot;https://x.com/kittysplit/status/1754481485736816847&quot;&gt;letzten Monat alle Anzeigen abgeschaltet&lt;/a&gt;. Kittysplit ist jetzt also werbefrei (juhu), bis wir eine andere Werbelösung finden oder Adsense das Problem behebt. Wir wissen, dass sowieso niemand Werbung mag, aber wir sind auf die Werbeeinnahmen angewiesen, damit kostenlose Kitties für dich kostenlos bleiben.&lt;/p&gt;

&lt;p&gt;Uns kostet das zwar Geld, weil die Werbeeinnahmen wegfallen, aber es ist uns wichtiger, unsere Nutzer vor Betrug durch unseriöse Werbetreibende zu schützen.&lt;/p&gt;

&lt;h2 id=&quot;aber-warum-passiert-das-überhaupt-noch&quot;&gt;Aber warum passiert das überhaupt noch?&lt;/h2&gt;

&lt;p&gt;Die eigentliche Frage ist, warum Adsense diese Anzeigen immer wieder zulässt. Sie verstoßen eindeutig gegen die Adsense-Richtlinien (&lt;a href=&quot;https://support.google.com/adspolicy/answer/6020955&quot;&gt;Falschdarstellung – Irreführendes Anzeigendesign&lt;/a&gt;): Anzeigen, bei denen Nutzer nur schwer erkennen können, dass sie es mit einer Anzeige zu tun haben, sind nicht erlaubt.&lt;/p&gt;

&lt;p&gt;Warum liefert Adsense sie also trotzdem aus? Solche Anzeigen sollten ziemlich leicht zu erkennen und zu blockieren sein. Ach komm, wir haben 2024, ihr seid Google und habt KI und so. Dass jeder Websitebetreiber unzählige Stunden damit verbringt, jede Anzeige einzeln zu blockieren, jedes Mal, wenn sie nach ein, zwei Tagen wieder auftaucht, kann ja wohl keine Lösung sein. Google/Adsense muss das auf seiner Seite lösen!&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;behauptet&lt;/a&gt;, Anzeigen mit Machine Learning und KI zu blockieren. Aber selbst die einfachste KI im Jahr 2024 sollte doch locker einen dicken grünen Button mit „WEITER“ erkennen können. Trotzdem kommen die Anzeigen immer wieder.&lt;/p&gt;

&lt;p&gt;Was ist los, Adsense?&lt;/p&gt;
</description>
        <pubDate>Thu, 28 Mar 2024 00:00:00 +0000</pubDate>
        <link>https://blog.kittysplit.com/de/adsense-ist-kaputt/</link>
        <guid isPermaLink="true">https://blog.kittysplit.com/de/adsense-ist-kaputt/</guid>
      </item>
    
  </channel>
</rss>
