< Zpět na články

Kotlin Multiplatform v praxi: jak sdílet kód mezi iOS a Androidem (a čemu se vyhnout)

Kotlin Multiplatform v praxi: jak sdílet kód mezi iOS a Androidem (a čemu se vyhnout)

📺 Záznam přednášky Rostislava Babáčka z Ackee Meets: Mobile Development:

Kotlin Multiplatform (KMP) slibuje něco jako svatý grál mobilního vývoje: napsat business logiku jednou a používat ji nativně na iOSu i Androidu. Zní to skvěle – a ono to skvělé opravdu být může. Cesta k tomu ale vede přes pár nečekaných crashů, jeden dva večery strávené hádkou s Gradlem a moment, kdy si iOSák poprvé otevře Android Studio a chvíli neví, kam vůbec klikat. My v Ackee jsme si tím prošli, když jsme spojili do té doby oddělené iOS a Android týmy nad jedním sdíleným kódem. Tady je upřímný přehled úskalí i toho, co reálně funguje.

📋 TL;DR

  • Kotlin Multiplatform (KMP) umožňuje sdílet business logiku, síťovou vrstvu a práci s daty mezi iOSem a Androidem, zatímco UI zůstává nativní.
  • „Stačí to jen napojit“ je mylná představa. Platformy se nechovají stejně, Kotlin má schované runtime pasti (first() vs. firstOrNull(), chybějící @Throws) a export do Swiftu není zadarmo.
  • Klíč není v technologii, ale v lidech a procesech: cílené workshopy, čas na vzdělávání, monorepo a společné analýzy obou platforem.
  • Osvědčené nástroje: SKIE (Swift-friendly API generátor od TouchLabu), Koin (DI), Detekt (statická analýza), Kotlin Native Xcode Plugin (debugging) a git worktree proti zbytečným buildům.
  • Přínos dobře nasazeného KMP: až ~30 % úspora času u featur se sdílenými ViewModely, sjednocené chování aplikací a vyšší zastupitelnost vývojářů.
Bugs Bunny a Daffy Duck se přetahují o ceduli s Apple logem – rivalita mezi iOS a Android tábory

🧩 Co je Kotlin Multiplatform (KMP)?

Kotlin Multiplatform je technologie od JetBrains, která umožňuje sdílet část kódu mezi platformami – typicky business logiku, síťovou vrstvu nebo práci s daty – a přitom si na každé platformě ponechat nativní UI. Sdílený kód se píše v Kotlinu a na iOS se exportuje jako framework, který se pak volá ze Swiftu.

Právě kombinace „sdílená logika + nativní vzhled“ je hlavní výhoda KMP oproti řešením jako Flutter nebo React Native, kde se sdílí i UI. Kotlin Multiplatform Mobile šel do alfy už v roce 2020, stabilní verze dorazila v roce 2023 – a od té doby je z KMP nástroj, který dává smysl nasazovat i na produkčních klientských projektech.

Cíl: aby se iOS (🍎) choval stejně jako Android (🤖)

⚖️ Kdy KMP dává smysl (a proč ne Flutter nebo React Native)

KMP se vyplatí tam, kde chcete jednotnou business logiku napříč platformami, ale nechcete slevit z nativního UI a výkonu. Sdílíte to, co má být na obou platformách stejné (logika, validace, práce s daty), a necháváte nativní to, co platformně specifické být má (vzhled, animace, systémové integrace).

Proto bývá KMP volbou i tam, kde by čistě „jedna codebase pro všechno“ nevyhovovala: nesdílíte UI vrstvu, takže se nevzdáváte nativního look & feel ani přístupu k platformním API. Bonus, na který se často zapomíná: s nativním UI zůstává nativní i přístupnost, kterou VoiceOver a TalkBack umí bez obcházení.

Tady je KMP v přímém srovnání s Flutterem a React Native:

VlastnostKotlin MultiplatformFlutterReact Native
Co se sdílíbusiness logika (UI zůstává nativní)logika i UIlogika i UI
UI vrstvaplně nativní (SwiftUI / Jetpack Compose)vlastní vykreslování (Skia)most do nativních komponent
Nativní vzhled a výkonzachovanýblízko nativu, ale vlastní renderdobrý, s režií mostu
Přístup k platformním APIpřímý a nativnípřes pluginypřes pluginy a mosty

Do téhle mapy mezitím vstoupil ještě jeden hráč, a to z opačného směru: Swift SDK for Android, které zkouší dostat Swift na Android. Zatím spíš experiment než produkční volba, ale směr stojí za sledování.


🚧 Nejčastější úskalí při zavádění KMP

Přechod z „píšu jen jednu platformu“ na „píšu i sdílený Kotlin“ má několik opakujících se úskalí. Tahle patří k nejčastějším.

Platformy se nechovají stejně

Sdílená logika neznamená, že se obě platformy chovají identicky. Krásný příklad je předávání dat přes navigaci: Android má limit na velikost dat posílaných přes navigaci, zatímco iOS ne. Řešení, které v pohodě funguje na iOSu (poslat objekt přímo), tak na Androidu spadne a je nutné data ukládat jinam (třeba do databáze) a přes navigaci posílat jen ID.

Pro tyhle případy je zásadní znát mechanismus expect/actual, který umožňuje mít ve sdíleném kódu společné rozhraní a platformně specifickou implementaci. Bez něj snadno naimplementujete „androidí“ workaround i na iOSu, kde ho vůbec nepotřebujete.

Rozdílná architektura: MVVM vs. Clean Architecture

iOS aplikace často stojí na MVVM (případně TCA), které řeší hlavně vztah View a ViewModelu. Android svět naopak běžně jede na Clean Architecture, a ta se přirozeně propíše i do sdíleného KMP kódu. Clean Architecture je výrazně striktnější na hranice mezi vrstvami – najednou zjistíte, že volat z jedné repozitory rovnou jinou není validní, o čemž vám MVVM nikdy neřeklo ani popel. Sladit architektonické zvyklosti obou platforem je jeden z prvních úkolů, který je potřeba vyřešit.

Skryté runtime pasti v Kotlinu

„Kotlin vypadá jako Swift, tohle dám levou zadní.“ Spoiler: nedáte. Kotlin má pasti, které kompilátor při přechodu neodhalí a projeví se až za běhu pěkným runtime crashem. Dvě nejčastější:

1. Chování first() na kolekci. Ve Swiftu vrací first optional. V Kotlinu first() na prázdné kolekci vyhodí NoSuchElementException:

list.first()          // ⚠️ NoSuchElementException, když je kolekce prázdná
list.firstOrNull()    // ✅ bezpečná varianta – vrací null

2. Neošetřené výjimky přes hranici do Swiftu. Ve Swiftu ze signatury poznáte, že funkce vyhazuje chybu, a kompilátor vás donutí ji ošetřit. V Kotlinu ne. Pokud u rozhraní, které může vyhodit výjimku, chybí anotace @Throws, swiftová strana o té výjimce vůbec neví – a je z toho crash:

public interface SetupHeartbeatObserverUseCase {
    @Throws(Throwable::class)          // ✅ bez této anotace Swift neví, že může přijít výjimka
    public suspend operator fun invoke()
}

Integrace a export do Swiftu

Po exportu z KMP nejsou vygenerovaná API přirozeně swiftová. Když potřebujete něco mockovat nebo pěkně integrovat, píšete kolem toho hromadu wrapperů a mapperů – a bez správně navržené architektury se z integrace stane menší očistec.

Vývojové prostředí a build frameworku

iOS vývojář zvyklý na Xcode narazí na neznámé Android Studio, jiné klávesové zkratky a na Gradle s desítkami tasků (a chvíli netuší, kde vůbec začít). Největší praktická brzda je ale build frameworku: při každé změně ve sdíleném KMP kódu je potřeba framework znovu sestavit – a to je čas, který byste radši věnovali čemukoli užitečnějšímu.

Homer Simpson bezradně u ovládacího pultu s popiskem „What do I do?“ – pocit iOSáka po prvním setkání s Android Studiem a Gradlem

🛠️ Jak úskalí překonat

Sladit dvě platformy nad jedním kódem není jen otázka nástrojů, ale hlavně lidí a procesů. Nám se osvědčilo pět věcí:

  1. Cílené workshopy – zkušenější platforma vede workshopy mířené na konkrétní projekty, ne na obecné koncepty. Vývojář si pak snadno najde paralelu se světem, který zná.
  2. Vyhrazený čas na vzdělávání – přechod na novou technologii chce prostor. V Ackee máme na vzdělávání 10 % pracovní doby, což lidem dává reálnou možnost se v KMP a Kotlinu zlepšit.
  3. Monorepo – dokud je sdílený kód jen submodul, tým ho bere jako externí knihovnu, která ho „nezajímá“. V monorepu má přehled o tom, co se ve sdílené vrstvě děje.
  4. Společné analýzy – u návrhu integrace mějte vždy zástupce iOSu i Androidu. Cílem je kompromis, ne aby jedna platforma diktovala řešení, které se druhé bude integrovat přes slzy.
  5. Přijetí reality – s tím, že to zpočátku bude trvat déle, musí počítat tým i projektové vedení. Návratnost přijde s časem.
Rostislav Babáček, mobilní vývojář v Ackee

Nástroje, které přechod usnadní

  • SKIE (od TouchLabu) – generátor Swift-friendly API nad KMP. Umožní ze Swiftu pohodlně sledovat stav KMP ViewModelu bez ručně psaných wrapperů.
  • Koin – dependency injection pro sdílený kód.
  • Detekt – statická analýza, kterou lze pokrýt řadu runtime pastí (třeba vynutit @Throws). Deterministické linty odhalí chybu dřív, než se z ní stane crash v produkci.
  • Kotlin Native Xcode Plugin (od TouchLabu) – dá vám breakpointy přímo do KMP kódu a náhled na stack trace i hodnoty proměnných místo dokola přidávaných printů a rebuildů.
  • git worktree – vedlejší klon repozitáře pro rychlé hašení incidentů; framework sestavíte jednou a při návratu na původní větev vám odpadá další build.
💡
Největší posun: přesuňte ViewModely do KMP

Čím víc logiky (včetně ViewModelů) přesunete do sdíleného kódu, tím míň jí zbyde na platformě – chování se sjednotí a opravy děláte na jednom místě, ne na dvou. Se SKIE k tomu navíc odpadají ruční wrappery kolem KMP ViewModelů.

🤖 Agentic development v KMP monorepu

Monorepo se sdíleným kódem se dobře snáší s AI-asistovaným vývojem. Osvědčují se:

  • Scoped rules – agent si do kontextu nenačítá pravidla pro Android/KMP, když se pracuje jen na iOS části (a naopak).
  • Skills na opakující se integrační úkoly (přidání KMP modulu, integrace ViewModelu na platformě).
  • Review agent na runtime chyby – jako doplněk, ne náhrada deterministických lintů.

Orchestrace více agentů je zatím diskutabilní – ještě se úplně neví, jestli agentovi pomáhá, nebo ho spíš mate.


👥 Organizační dopad: z platformních týmů projektové

Sdílení kódu má i organizační přesah. Když iOS i Android vývojáři pracují nad stejnou logikou, přestává dávat smysl dělit týmy podle platformy. V Ackee jsme proto přešli z oddělených iOS a Android týmů na mobilní týmy zaměřené na projekty: každý tým je mix obou platforem, sdílí know-how a kontext k projektům a platformní světy se postupně sbližují. Nad tím pomáhá pravidelný společný all-hands s technologickými novinkami.

Výsledkem je vyšší zastupitelnost: přijde incident a vyřešit ho může kterýkoli mobilní vývojář, ne jen „ten od dané platformy“.

Dvě nadšeně tleskající děti – radost z toho, že z platformních týmů jsou spolupracující mobilní týmy

📈 Jaké to má výsledky

Dobře nasazený KMP přináší měřitelné i „měkké“ benefity:

  • Až ~30 % úspora času (interní odhad z našich projektů, ne měření) u featur, kde je logika (včetně ViewModelů) ve sdíleném kódu.
  • Sjednocené chování aplikací – obě platformy se chovají stejně, což ocení QA i klient.
  • Vyšší zastupitelnost vývojářů a méně duplicitní logiky, kterou by jinak bylo nutné opravovat na dvou místech.
⚠️
Těch 30 % berte s rezervou

Úspora není univerzální. U některých featur neušetříte nic, jinde jde o jednotky procent – a dokud logika (třeba ViewModely) není ve sdíleném kódu, může být přínos nulový i záporný. Rozdíl dělá dobrý návrh a poctivá analýza.

❓ FAQ

Co je Kotlin Multiplatform (KMP)?

Technologie od JetBrains pro sdílení kódu (business logiky, síťové vrstvy, práce s daty) mezi iOSem a Androidem, při zachování nativního UI na každé platformě.

Jaký je rozdíl mezi KMP a Flutterem nebo React Native?

KMP sdílí jen business logiku, UI zůstává nativní. Flutter a React Native sdílejí i UI vrstvu. KMP proto volí týmy, které nechtějí slevit z nativního vzhledu a výkonu.

Proč aplikace v KMP crashuje na first()?

Kotlinovská first() na prázdné kolekci vyhazuje NoSuchElementException. Používejte firstOrNull(), která vrací null.

K čemu je anotace @Throws v KMP?

Bez ní swiftová strana neví, že sdílená funkce může vyhodit výjimku, a přijde runtime crash. @Throws výjimku „zviditelní“ pro interoperabilitu se Swiftem.

Kolik času KMP reálně ušetří?

U featur se sdílenými ViewModely v praxi až ~30 %. Úspora ale závisí na návrhu; u některých featur je minimální nebo žádná.

Jaké nástroje se pro KMP vyplatí?

SKIE (Swift-friendly API generátor), Koin (DI), Detekt (statická analýza), Kotlin Native Xcode Plugin (debugging) a git worktree (rychlejší práce s buildy).

🎯 Co si odnést

Sdílení kódu mezi iOSem a Androidem přes Kotlin Multiplatform není jen technické „napojení“. Je to změna technologická i organizační, která stojí na ochotě obou platforem sladit architekturu, poctivě analyzovat a smířit se s tím, že návratnost přijde postupně. Když to ustojíte, výsledkem jsou konzistentní aplikace, zastupitelní vývojáři a mobilní tým, který táhne za jeden provaz – i když se u prvních crashů zapotíte.

Celý příběh i s diskuzí z publika najdete v záznamu přednášky. Termíny dalších Ackee Meets sledujte na Eventbritu a přihlaste se k newsletteru – příští takový obsah vám přijde přímo do schránky.

Rostislav Babáček
Rostislav Babáček
Team Leader Indigo Mobile Development týmuRosťa je Team Leader Indigo Mobile Development týmu, vrchní implementátor Agentic Developmentu v Ackee a externí lektor na FIT ČVUT. Ve světě mobilního vývoje se pohybuje přes 7 let a má za sebou projekty od vládních aplikací po streamovací platformy. Když zrovna neladí (AI) kód pravděpodobně ho najdete na treku v horách, na tréninku nebo cestujícího s batohem po celém světě.

Máte zájem o spolupráci? Pojďme to probrat osobně!

Napište nám >