<?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/pl/</link>
    <language>pl</language>
    <atom:link href="https://blog.kittysplit.com/pl/feed.xml" rel="self" type="application/rss+xml" />
    
      <item>
        <title>Konta w Kittysplit - twoje Kitty bezpieczne i pod ręką</title>
        <description>&lt;p&gt;Konta są już dostępne. Zaloguj się w Kittysplit (w przeglądarce, na iOS albo na Androidzie), a wszystkie twoje Kitty pójdą za tobą. To nasza największa aktualizacja od dłuższego czasu i jest całkowicie opcjonalna.&lt;/p&gt;

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

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

&lt;h2 id=&quot;po-co-konta&quot;&gt;Po co konta?&lt;/h2&gt;

&lt;p&gt;Przez długi czas Kittysplit działał bez żadnych kont i to było celowe. Brak konta oznacza brak rejestracji, żadnych haseł do zapamiętania i żadnego zbierania danych osobowych. Do tego wtedy &lt;a href=&quot;/webapp-vs-native-app&quot;&gt;nie mieliśmy jeszcze apek mobilnych&lt;/a&gt;, więc przesiadanie się między urządzeniami nie było aż takim problemem.&lt;/p&gt;

&lt;p&gt;Ale w naszej ankiecie konta były prośbą numer jeden, a od &lt;a href=&quot;/pl/aplikacja-mobilna-kittysplit/&quot;&gt;premiery apek&lt;/a&gt; w zeszłym roku słyszeliśmy o nich bez przerwy. No to wreszcie są! Samo wprowadzenie kont to żadna wielka filozofia. Schody zaczynają się wtedy, gdy mają pozostać opcjonalne. Sporo się nagłowiliśmy, żeby każdy Kitty działał tak samo, niezależnie od tego, czy jesteś zalogowany, czy nie.&lt;/p&gt;

&lt;h2 id=&quot;twoje-kitty-na-każdym-urządzeniu&quot;&gt;Twoje Kitty na każdym urządzeniu&lt;/h2&gt;

&lt;p&gt;Zaloguj się w przeglądarce albo na telefonie, a wszędzie zobaczysz te same Kitty. Koniec z przekopywaniem starych czatów w poszukiwaniu linku. Nowy telefon? Inna przeglądarka? Po prostu się zaloguj i działaj dalej tam, gdzie skończyłeś.&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;Zaloguj się lub zarejestruj&quot; title=&quot;Zaloguj się lub zarejestruj&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;Twój profil&quot; title=&quot;Twój 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;Zaloguj się lub zarejestruj&lt;/em&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;em&gt;Twój profil&lt;/em&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2 id=&quot;bezpieczniejsze-kitty&quot;&gt;Bezpieczniejsze Kitty&lt;/h2&gt;

&lt;p&gt;Jeśli założysz Kitty po zalogowaniu, jest twój. Tylko ty możesz go edytować albo usunąć, więc nic ważnego nie zniknie przez przypadek. Twoi znajomi dalej mogą dodawać wydatki i się rozliczać, tak jak do tej pory. Dla nich nic się nie zmienia.&lt;/p&gt;

&lt;h2 id=&quot;twoi-znajomi-nie-potrzebują-konta&quot;&gt;Twoi znajomi nie potrzebują konta&lt;/h2&gt;

&lt;p&gt;Konto to twój wybór i tak już zostanie. Zapraszaj ludzi tak jak zawsze: wyślij im link i od razu mogą zacząć dzielić wydatki. Bez rejestracji, bez kombinowania. To wciąż serce Kittysplit.&lt;/p&gt;

&lt;h2 id=&quot;już-wkrótce-zdjęcia-profilowe&quot;&gt;Już wkrótce: zdjęcia profilowe&lt;/h2&gt;

&lt;p&gt;Następne w kolejce są zdjęcia profilowe: wrzuć fajną fotkę, żeby w Kitty twoich znajomych być kimś więcej niż tylko imieniem. Mamy też w zanadrzu kilka innych pomysłów tylko dla osób z kontem.&lt;/p&gt;

&lt;p&gt;Miłego dzielenia wydatków! 🐱✨&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;Masz uwagi albo pomysły? Odezwij się na &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;Instagramie&lt;/a&gt; albo napisz maila na 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/pl/konta-w-kittysplit/</link>
        <guid isPermaLink="true">https://blog.kittysplit.com/pl/konta-w-kittysplit/</guid>
      </item>
    
      <item>
        <title>Załączniki do wydatków - dodawaj zdjęcia do swoich wydatków</title>
        <description>&lt;p&gt;W Kittysplit pojawiły się załączniki do wydatków! Do każdego wydatku w swoim Kitty możesz teraz dołączyć maksymalnie 3 zdjęcia. To funkcja &lt;strong&gt;Super Kitty&lt;/strong&gt;, więc żeby z niej korzystać, musisz uaktualnić swojego Kitty.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2026-03-12/expense-attachments-1.png&quot; alt=&quot;Załączniki do wydatków&quot; title=&quot;Załączniki do wydatków&quot; width=&quot;320&quot; /&gt;&lt;/p&gt;

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

&lt;h2 id=&quot;co-nowego&quot;&gt;Co nowego?&lt;/h2&gt;

&lt;p&gt;Do każdego wydatku możesz teraz dodać zdjęcia. Pstryknij paragon, rachunek z restauracji albo cokolwiek, co dzielicie, a zdjęcie zostanie przy wydatku i każdy w grupie je zobaczy.&lt;/p&gt;

&lt;p&gt;Na jeden wydatek przypadają maksymalnie 3 zdjęcia, co powinno wystarczyć w większości sytuacji. Paragony, faktury z hotelu, bilety na koncert albo po prostu zdjęcie tego, co kupiłeś, żeby reszta wiedziała, na co się zrzuca.&lt;/p&gt;

&lt;p&gt;Koniec z przekopywaniem galerii kilka tygodni później w poszukiwaniu tego jednego paragonu. Dodaj go od razu razem z wydatkiem i gotowe.&lt;/p&gt;

&lt;h2 id=&quot;jak-to-działa&quot;&gt;Jak to działa&lt;/h2&gt;

&lt;p&gt;Kiedy dodajesz albo edytujesz wydatek, zobaczysz opcję dołączenia zdjęć. Wybierz zdjęcie z galerii. Jeśli później chcesz je podmienić, po prostu usuń stare i dodaj nowe.&lt;/p&gt;

&lt;h2 id=&quot;wypróbuj&quot;&gt;Wypróbuj&lt;/h2&gt;

&lt;p&gt;Uaktualnij do Super Kitty i dodaj zdjęcia do kolejnego wydatku. To wszystko!&lt;/p&gt;

&lt;p&gt;Miłego dzielenia wydatków! 🐱✨&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;Masz uwagi albo pomysły? Odezwij się na &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;Instagramie&lt;/a&gt; albo napisz maila na 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/pl/zalaczniki-do-wydatkow/</link>
        <guid isPermaLink="true">https://blog.kittysplit.com/pl/zalaczniki-do-wydatkow/</guid>
      </item>
    
      <item>
        <title>Kategorie wydatków - zobacz, na co idą pieniądze w twoich Kitty</title>
        <description>&lt;p&gt;Mamy dla was nową, dużą funkcję w Kittysplit: &lt;strong&gt;kategorie wydatków&lt;/strong&gt;! Od dziś każdy wydatek i przychód w twoich Kitty możesz przypisać do kategorii, więc łatwiej zobaczysz, na co właściwie idą pieniądze.&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;Dodaj wydatek&quot; title=&quot;Dodaj wydatek&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;Filtruj według kategorii&quot; title=&quot;Filtruj według kategorii&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;Dodaj wydatek&lt;/em&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;em&gt;Filtruj według kategorii&lt;/em&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

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

&lt;h2 id=&quot;co-nowego&quot;&gt;Co nowego?&lt;/h2&gt;

&lt;p&gt;Każdy wydatek albo przychód, który dodajesz do Kitty, możesz teraz przypisać do jednej z 12 kategorii:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Mieszkanie i dom&lt;/strong&gt; - czynsz, kredyt hipoteczny, ubezpieczenie domu i podstawowe rzeczy do domu&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Zakwaterowanie&lt;/strong&gt; - hotele, wynajem wakacyjny i noclegi na krótko&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Jedzenie i zakupy&lt;/strong&gt; - wypady do supermarketu i zapasy jedzenia do domu&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Restauracje i wyjścia&lt;/strong&gt; - restauracje, bary, kawiarnie i jedzenie na wynos&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Transport&lt;/strong&gt; - paliwo, komunikacja miejska, przejazdy typu Uber i wynajem auta&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Media i łączność&lt;/strong&gt; - prąd, woda, internet i rachunki za telefon&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Zdrowie&lt;/strong&gt; - lekarze, apteka i ubezpieczenie zdrowotne&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Rozrywka i aktywności&lt;/strong&gt; - rozrywka, atrakcje, koncerty i wydarzenia&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Zakupy i sprzęt&lt;/strong&gt; - ubrania, elektronika i zakupy różne&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Wellness i pielęgnacja&lt;/strong&gt; - siłownia, spa, kosmetyki i dbanie o siebie&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Finanse i opłaty&lt;/strong&gt; - opłaty bankowe, prowizje i inne koszty finansowe&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Różne&lt;/strong&gt; - wszystko, co nie pasuje nigdzie indziej&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Każda kategoria ma swój kolor, więc wzorce widać na pierwszy rzut oka.&lt;/p&gt;

&lt;h2 id=&quot;filtruj-swoje-wydatki&quot;&gt;Filtruj swoje wydatki&lt;/h2&gt;

&lt;p&gt;Pierwsza funkcja, którą wprowadzamy razem z kategoriami, to &lt;strong&gt;filtrowanie wydatków&lt;/strong&gt;. Listę wydatków możesz teraz przefiltrować według kategorii i szybko znaleźć:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;wszystkie wydatki na &lt;strong&gt;restauracje&lt;/strong&gt; z waszego wspólnego wyjazdu&lt;/li&gt;
  &lt;li&gt;wydatki na &lt;strong&gt;zakupy spożywcze&lt;/strong&gt; we wspólnym mieszkaniu&lt;/li&gt;
  &lt;li&gt;koszty &lt;strong&gt;transportu&lt;/strong&gt; podczas waszego road tripu&lt;/li&gt;
  &lt;li&gt;dowolną kategorię, którą chcesz przejrzeć&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Dzięki temu łatwo odpowiesz na pytania typu „Ile wydaliśmy na jedzenie?” albo „Ile kosztowały nas noclegi na wakacjach?”.&lt;/p&gt;

&lt;h2 id=&quot;co-dalej&quot;&gt;Co dalej?&lt;/h2&gt;

&lt;p&gt;To dopiero początek. Już pracujemy nad kolejnymi funkcjami, które wyciągną z kategorii jeszcze więcej:&lt;/p&gt;

&lt;h3 id=&quot;porównania-miesiąc-do-miesiąca&quot;&gt;Porównania miesiąc do miesiąca&lt;/h3&gt;
&lt;p&gt;Zobacz, jak zmieniają się twoje wydatki w każdej kategorii. Czy rachunki za zakupy po cichu rosną, a może w tym miesiącu wydajesz mniej na jedzenie na mieście niż w poprzednim?&lt;/p&gt;

&lt;h3 id=&quot;analiza-trendów&quot;&gt;Analiza trendów&lt;/h3&gt;
&lt;p&gt;Wyłapuj wzorce i trendy w wydatkach z tygodni, miesięcy, a nawet lat. Idealne dla współlokatorów, którzy chcą lepiej ogarnąć wspólny domowy budżet.&lt;/p&gt;

&lt;h3 id=&quot;raporty-graficzne&quot;&gt;Raporty graficzne&lt;/h3&gt;
&lt;p&gt;Czytelne wykresy pokazujące, jak wydatki rozkładają się na kategorie. Wykresy kołowe dla ogólnego podziału, słupkowe do porównywania kategorii i osie czasu dla trendów.&lt;/p&gt;

&lt;h2 id=&quot;po-co-w-ogóle-kategorie&quot;&gt;Po co w ogóle kategorie?&lt;/h2&gt;

&lt;p&gt;Niezależnie od tego, czy mieszkasz ze współlokatorami, czy planujesz wspólne wakacje, kategorie pomagają:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;zrozumieć, jak wydajecie&lt;/strong&gt; - zobaczysz, gdzie naprawdę idą wspólne pieniądze&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;planować mądrzej&lt;/strong&gt; - na podstawie wcześniejszych wydatków łatwiej zaplanujesz budżet na podobny wyjazd czy imprezę&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;mieć wszystko jasne&lt;/strong&gt; - każdy w grupie widzi, ile poszło na którą kategorię&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;zacznij-już-dziś&quot;&gt;Zacznij już dziś&lt;/h2&gt;

&lt;p&gt;Kategorie są dostępne w najnowszej wersji Kittysplit. Po prostu dodaj albo edytuj wydatek, a zobaczysz opcję wyboru kategorii. Tylko tyle!&lt;/p&gt;

&lt;p&gt;Jak zawsze chętnie poznamy twoją opinię i pomysły na kolejne ulepszenia.&lt;/p&gt;

&lt;p&gt;Miłego dzielenia wydatków! 🐱✨&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;Masz pomysł, jak kategorie mogłyby lepiej pasować do tego, jak z nich korzystasz? Odezwij się na &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;Instagramie&lt;/a&gt; albo napisz maila na 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/pl/kategorie-wydatkow/</link>
        <guid isPermaLink="true">https://blog.kittysplit.com/pl/kategorie-wydatkow/</guid>
      </item>
    
      <item>
        <title>Nowe podsumowanie i kocie awatary</title>
        <description>&lt;p&gt;Tuż przed świętami 2025 apki Kittysplit dostają aktualizację (v1.3). Skupiamy się w niej na dwóch rzeczach, które od dawna chcieliśmy poprawić:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Nowe podsumowanie&lt;/strong&gt;, które pokazuje to, co ważne, zamiast zasypywać cię liczbami&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Kocie awatary&lt;/strong&gt;, dzięki którym wszystko wygląda przyjaźniej i weselej&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;podsumowanie-przed-i-po&quot;&gt;Podsumowanie: przed i po&lt;/h2&gt;

&lt;p&gt;Podsumowanie to pierwsze, co widzisz po otwarciu Kitty. Przebudowaliśmy je tak, żeby szybko odpowiadało na najważniejsze pytania: ile kosztowało całe wydarzenie? Kto komu ile jest winien? Ogólnie staraliśmy się nadać wszystkiemu więcej struktury dzięki kartom z Material Design. Miejscami wyszliśmy z tym nawet poza samo podsumowanie.&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;Podsumowanie Kitty - przed&quot; title=&quot;Podsumowanie Kitty - przed&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;Podsumowanie Kitty - po&quot; title=&quot;Podsumowanie Kitty - po&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;Podsumowanie Kitty - przed&lt;/em&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;em&gt;Podsumowanie Kitty - po&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;Podsumowanie Kitty - po (ciemny motyw)&quot; title=&quot;Podsumowanie Kitty - po (ciemny motyw)&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;Zarządzaj uczestnikami - po (ciemny motyw)&quot; title=&quot;Zarządzaj uczestnikami - po (ciemny motyw)&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;Podsumowanie Kitty - po (ciemny motyw)&lt;/em&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;em&gt;Zarządzaj uczestnikami - po (ciemny motyw)&lt;/em&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2 id=&quot;kocie-awatary-dla-każdej-osoby&quot;&gt;Kocie awatary dla każdej osoby&lt;/h2&gt;

&lt;p&gt;W każdym Kitty każda osoba dostaje teraz własnego, niepowtarzalnego kociego awatara. Tak, pasuje to do nazwy Kittysplit, ale są też praktyczne powody.&lt;/p&gt;

&lt;p&gt;Obrazki pomagają ogarnąć informacje szybciej niż sam tekst. Dzięki kotom:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;apka mniej przypomina arkusz kalkulacyjny&lt;/li&gt;
  &lt;li&gt;łatwiej rozpoznać osoby w większych grupach&lt;/li&gt;
  &lt;li&gt;od razu widać, kto za co zapłacił, kiedy przewijasz wydatki&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;Kocie awatary (przykład)&quot; title=&quot;Kocie awatary (przykład)&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;Kocie awatary (przykład)&lt;/em&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2 id=&quot;co-się-nie-zmieniło&quot;&gt;Co się nie zmieniło&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Nie trzeba się rejestrować&lt;/li&gt;
  &lt;li&gt;Działa w przeglądarce, na iOS i na Androidzie&lt;/li&gt;
  &lt;li&gt;Sprytne liczenie długów&lt;/li&gt;
  &lt;li&gt;Większość funkcji za darmo&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;pobierz-aktualizację&quot;&gt;Pobierz aktualizację&lt;/h2&gt;

&lt;p&gt;Jeśli korzystasz z apki, aktualizacja przyjdzie przez App Store albo Google Play.&lt;/p&gt;

&lt;p&gt;Daj nam znać, co o tym myślisz: pisz na info[at]kittysplit.com albo przez przycisk opinii w apce.&lt;/p&gt;
</description>
        <pubDate>Tue, 09 Dec 2025 00:00:00 +0000</pubDate>
        <link>https://blog.kittysplit.com/pl/nowe-podsumowanie-i-kocie-awatary/</link>
        <guid isPermaLink="true">https://blog.kittysplit.com/pl/nowe-podsumowanie-i-kocie-awatary/</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 w telefonie: poznaj nasze apki na Androida i iOS</title>
        <description>&lt;p&gt;Po ponad dziesięciu latach działania wyłącznie w przeglądarce Kittysplit wreszcie ma natywne apki mobilne na Androida i iOS! Od startu w 2012 roku pomagamy znajomym, współlokatorom i ekipom wyjazdowym dzielić wydatki uczciwie i bez zawracania głowy, do tej pory przez naszą aplikację webową.&lt;/p&gt;

&lt;h2 id=&quot;dlaczego-apki-i-czemu-akurat-teraz&quot;&gt;Dlaczego apki? I czemu akurat teraz?&lt;/h2&gt;

&lt;p&gt;Od zawsze chcieliśmy, żeby dzielenie wydatków było jak najbardziej dostępne. Przez lata nasza aplikacja webowa dobrze się sprawdzała: każdy, kto ma przeglądarkę, mógł bez problemu ogarniać wspólne wydatki w grupie. Ale słuchaliśmy, co do nas piszecie, i wiemy, że wielu z was po prostu woli wygodę osobnej apki w telefonie.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Szybki dostęp&lt;/strong&gt;: do wspólnych wydatków bez otwierania przeglądarki&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Płynniejsze działanie&lt;/strong&gt;: wszystko dopasowane specjalnie do telefonów&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;Podsumowanie Kitty na iOS&quot; title=&quot;Podsumowanie Kitty na iOS&quot; width=&quot;320&quot; /&gt; &lt;img src=&quot;/images/2025-09-10-mobile-app-release--app-entries.png&quot; alt=&quot;Wydatki w Kitty na iOS&quot; title=&quot;Wydatki w Kitty na iOS&quot; width=&quot;320&quot; /&gt;&lt;/p&gt;

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

&lt;h2 id=&quot;wciąż-ten-sam-kittysplit-który-lubisz&quot;&gt;Wciąż ten sam Kittysplit, który lubisz&lt;/h2&gt;

&lt;p&gt;Bardzo się cieszymy z apek, ale spokojnie: Kittysplit działa tak samo jak wcześniej. Dalej stawiamy na:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Prostotę:&lt;/strong&gt; żadnych skomplikowanych funkcji, których nie potrzebujesz.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Brak rejestracji:&lt;/strong&gt; tworzysz Kitty bez zakładania konta.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Uczciwy podział:&lt;/strong&gt; kilka sposobów, żeby sprawiedliwie podzielić wydatki.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Darmowe podstawy:&lt;/strong&gt; najważniejsze funkcje nadal są za darmo.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Apka nie jest obowiązkowa:&lt;/strong&gt; apki mają po prostu ułatwić życie w telefonie, a Kittysplit w przeglądarce zostaje z nami.&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;Kitty na wakacjach&quot; title=&quot;Kitty na wakacjach&quot; width=&quot;500&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;przeglądarka-zostaje&quot;&gt;Przeglądarka zostaje&lt;/h2&gt;

&lt;p&gt;Lubimy, kiedy macie wybór. Choć bardzo się cieszymy, że wreszcie możemy wam dać apki, chcemy to powiedzieć wyraźnie: &lt;strong&gt;wersji webowej nigdzie nie porzucamy&lt;/strong&gt;. Rozumiemy, że nie każdy chce instalować kolejną apkę, a dla wielu z was wygoda przeglądarki jest po prostu nie do przecenienia.&lt;/p&gt;

&lt;p&gt;Wersja webowa Kittysplit dalej będzie dostawać tyle samo uwagi, aktualizacji i nowych funkcji co apki.&lt;/p&gt;

&lt;h2 id=&quot;pobierz-już-dziś&quot;&gt;Pobierz już dziś!&lt;/h2&gt;

&lt;p&gt;Nasze nowe apki są już dostępne w App Store i w Google Play. Niezależnie od tego, czy korzystasz z Kittysplit od lat, czy dopiero nas poznajesz, zajrzyj do apki, a jeśli wolisz, dalej korzystaj z wersji w przeglądarce.&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;Pobierz z 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;Pobierz z 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;Albo korzystaj dalej z naszej aplikacji webowej na &lt;a href=&quot;https://www.kittysplit.com&quot;&gt;kittysplit.com&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;co-dalej&quot;&gt;Co dalej?&lt;/h2&gt;
&lt;p&gt;Premiera apek to dopiero początek. Już pracujemy nad poprawkami i nowymi funkcjami, które biorą się z waszych opinii.&lt;/p&gt;

&lt;p&gt;Chętnie usłyszymy, co myślicie o nowych apkach i co jeszcze moglibyśmy ulepszyć! Jak zawsze chcemy, żeby dzielenie wydatków było jak najprostsze i bez stresu, a teraz na jeszcze większej liczbie urządzeń.&lt;/p&gt;
</description>
        <pubDate>Wed, 10 Sep 2025 00:00:00 +0000</pubDate>
        <link>https://blog.kittysplit.com/pl/aplikacja-mobilna-kittysplit/</link>
        <guid isPermaLink="true">https://blog.kittysplit.com/pl/aplikacja-mobilna-kittysplit/</guid>
      </item>
    
      <item>
        <title>Adsense jest zepsuty</title>
        <description>&lt;p&gt;Już &lt;a href=&quot;/pl/adsense-i-oszukancze-reklamy/&quot;&gt;kilka lat temu&lt;/a&gt; mieliśmy kłopoty z oszukańczymi reklamami w Adsense. Tak oczywiste oszustwa jak fałszywe ostrzeżenie o wirusie z 2018 roku chyba zniknęły, ale pojawiły się inne oszukańcze reklamy.&lt;/p&gt;

&lt;p&gt;W zeszłym roku napisała do nas osoba, która chciała anulować swoją subskrypcję, tyle że Kittysplit nie ma żadnych subskrypcji. Szybko się zorientowaliśmy, że ta osoba padła ofiarą oszukańczej reklamy, która wyświetliła się jej podczas korzystania z Kittysplit na telefonie.&lt;/p&gt;

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

&lt;h2 id=&quot;jak-to-oszustwo-działało&quot;&gt;Jak to oszustwo działało&lt;/h2&gt;

&lt;p&gt;Schemat jest prosty: reklama pokazuje ogromny zielony przycisk &lt;strong&gt;START&lt;/strong&gt; i udaje, że jest częścią Kittysplit. Ale przycisk prowadzi na zewnętrzną stronę, która prosi o dane karty kredytowej. Strona udaje, że opłata dotyczy jakiejś usługi Kittysplit (np. rozliczenia ze znajomymi albo korzystania ze strony). Na początku jest za darmo albo prawie za darmo, ale potem zaczyna pobierać miesięczną subskrypcję, od 20 do 60 €/$/£, za usługę, której nikt nie chciał.&lt;/p&gt;

&lt;p&gt;Oto zrzut ekranu takiej zwodniczej reklamy:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2024-03-adsense-scam.jpg&quot; alt=&quot;Przykład wprowadzającej w błąd reklamy&quot; title=&quot;Przykład wprowadzającej w błąd reklamy&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Kiedy dowiedzieliśmy się o problemie, szybko zareagowaliśmy i zaczęliśmy blokować takie reklamy w centrum weryfikacji reklam Adsense. Przez jakiś czas wyglądało na to, że to pomogło, ale reklamy wracały w różnych wariantach, zawsze z dużym, jaskrawym przyciskiem &lt;strong&gt;START&lt;/strong&gt;, &lt;strong&gt;CONTINUE&lt;/strong&gt;, &lt;strong&gt;NEXT&lt;/strong&gt;, &lt;strong&gt;DOWNLOAD&lt;/strong&gt; albo podobnym.&lt;/p&gt;

&lt;p&gt;Kilka miesięcy temu odezwała się kolejna osoba, którą też oszukały te reklamy. Znowu blokowaliśmy wprowadzające w błąd reklamy, raz za razem. Spędziliśmy wiele godzin na blokowaniu tysięcy reklam i setek kont reklamodawców, licząc, że to rozwiąże problem. Ale to się po prostu nie kończyło: nieważne, ile razy blokowaliśmy te reklamy, po kilku dniach wracały.&lt;/p&gt;

&lt;h2 id=&quot;dlaczego-postanowiliśmy-wyłączyć-reklamy-żeby-chronić-osoby-korzystające-z-kittysplit&quot;&gt;Dlaczego postanowiliśmy wyłączyć reklamy, żeby chronić osoby korzystające z Kittysplit&lt;/h2&gt;

&lt;p&gt;Okazuje się, że nie da się wyłapać wszystkich złych reklam, a niedawno oszukano trzecią osobę. Uznaliśmy więc, że tego już za wiele, i &lt;a href=&quot;https://x.com/kittysplit/status/1754481485736816847&quot;&gt;w zeszłym miesiącu wyłączyliśmy wszystkie reklamy&lt;/a&gt;. Kittysplit jest więc teraz wolny od reklam (hurra!), dopóki nie znajdziemy innego rozwiązania reklamowego albo Adsense nie naprawi problemu. Wiemy, że i tak nikt nie lubi reklam, ale to dzięki przychodom z reklam darmowe Kitty mogą dla ciebie zostać darmowe.&lt;/p&gt;

&lt;p&gt;Tracimy przez to pieniądze z reklam, ale dla nas ważniejsze jest, żeby chronić ludzi przed oszustwami nieuczciwych reklamodawców.&lt;/p&gt;

&lt;h2 id=&quot;ale-dlaczego-to-wciąż-się-dzieje&quot;&gt;Ale dlaczego to wciąż się dzieje?&lt;/h2&gt;

&lt;p&gt;Prawdziwe pytanie brzmi: dlaczego Adsense wciąż pozwala tym reklamom wracać? Wyraźnie łamią zasady Adsense (&lt;a href=&quot;https://support.google.com/adspolicy/answer/6020955&quot;&gt;Misrepresentation - Misleading ad design&lt;/a&gt;): reklamy, przy których trudno zorientować się, że to reklama, są niedozwolone.&lt;/p&gt;

&lt;p&gt;Dlaczego więc Adsense wciąż je wyświetla? Takie reklamy powinno być dość łatwo wykryć i zablokować. No weźcie, mamy 2024 rok, jesteście Google, macie AI i w ogóle. To, że każdy właściciel strony spędza niezliczone godziny na blokowaniu każdej reklamy z osobna za każdym razem, gdy po dniu czy dwóch wraca, to oczywiście żadne rozwiązanie. Google i Adsense muszą to załatwić po swojej stronie!&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;twierdzi&lt;/a&gt;, że do blokowania reklam używa uczenia maszynowego i AI. Ale nawet najbardziej prymitywna AI w 2024 roku powinna bez trudu rozpoznać wielki, tłusty zielony przycisk z napisem „CONTINUE”. A reklamy dalej wracają.&lt;/p&gt;

&lt;p&gt;Co jest nie tak, Adsense?&lt;/p&gt;
</description>
        <pubDate>Thu, 28 Mar 2024 00:00:00 +0000</pubDate>
        <link>https://blog.kittysplit.com/pl/adsense-jest-zepsuty/</link>
        <guid isPermaLink="true">https://blog.kittysplit.com/pl/adsense-jest-zepsuty/</guid>
      </item>
    
  </channel>
</rss>
