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ářů.

🧩 Co je Kotlin Multiplatform (KMP)?
Za tímhle názvem stojí JetBrains, tvůrci samotného Kotlinu. 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.

⚖️ 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:
| Vlastnost | Kotlin Multiplatform | Flutter | React Native |
|---|---|---|---|
| Co se sdílí | business logika (UI zůstává nativní) | logika i UI | logika i UI |
| UI vrstva | plně nativní (SwiftUI / Jetpack Compose) | vlastní vykreslování (Impeller, dříve Skia) | nativní komponenty přes JS rozhraní (JSI) |
| Nativní vzhled a výkon | zachovaný | blízko nativu, ale vlastní render | dobrý, s režií JS vrstvy |
| Přístup k platformním API | přímý a nativní | přes pluginy | přes nativní moduly |
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í null2. 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. (Řešení, které tuhle práci z velké části odstraní, je nástroj SKIE – viz sekci o nástrojích níže.)
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.
