KMP: где заканчивается общий код и начинаются проблемы
Kotlin Multiplatform реально экономит код только в бизнес-логике, сетевых запросах и моделях данных — обычно это ядро приложения. UI, доступ к камере, биометрии, push-уведомлениям, фоновым задачам и хранилищу почти всегда остаётся платформенным. Проблемы начинаются, когда команда пытается расширить общий код на эти зоны вместо того, чтобы явно провести границу между shared-модулем и нативным слоем.
Почему возникают проблемы на границе shared-кода и platform-API
Kotlin Multiplatform компилирует общий модуль в JVM-байткод для Android и в нативный бинарник для iOS через Kotlin/Native. Любая функция, которой нужен доступ к системным API — камере, биометрии, пуш-токену, — описывается как expect в общем коде и реализуется дважды через actual, отдельно под каждую платформу. Маркетинг вокруг KMP часто обещает единый код на 80-90% приложения, и заказчик закладывает бюджет исходя из этой цифры. На практике проблема начинается именно здесь: команда пытается расширить общий код за естественные границы, заворачивает платформенные различия в один интерфейс, чтобы было удобнее, и теряет контроль над деталями, которые на самом деле различаются на каждой ОС.
На практике это выглядит так: логистическое или торговое приложение для среднего бизнеса в Москве почти всегда включает сканирование штрихкодов через камеру, push-уведомления о статусе заказа и офлайн-режим с последующей синхронизацией. Бизнес-правила и логика синхронизации действительно шарятся между iOS и Android. А вот сканер штрихкодов, поведение в офлайне и работа push-уведомлений в фоне пишутся и тестируются отдельно под каждую платформу — и именно здесь всплывает большая часть багов на приёмке.
Разные модели памяти и жизненного цикла
iOS использует ARC, Android — сборщик мусора JVM. Kotlin/Native годами менял модель памяти, и код, который стабильно работал на Android, падал на iOS из-за иных правил владения объектами между потоками. Фоновые задачи тоже ведут себя по-разному: iOS агрессивно ограничивает фоновое выполнение, Android — более гибко, и общий код, рассчитанный на одну модель, тихо перестаёт работать на другой.
Нет единого API для железа и системных сервисов
Push-уведомления идут через APNs на iOS и FCM на Android — это два разных протокола с разной доставкой и форматом токена. Биометрия — Face ID и Touch ID против BiometricPrompt. Диалоги разрешений отличаются по сценарию и по тому, что происходит при повторном отказе пользователя. Обёртка в общий интерфейс не убирает эти различия — она просто прячет их до первого прод-инцидента.
Разный релизный цикл App Store и Google Play
Даже если баг исправлен в общем коде одной строкой, до пользователей он доедет по-разному. Android-сборку можно выкатить поэтапным роллаутом за часы. iOS-сборка проходит модерацию Apple, которая может занять от нескольких часов до нескольких дней, а при отклонении — дольше. В результате один и тот же фикс на практике оказывается фиксом с разным SLA на каждой платформе, и это нужно закладывать в план поддержки, а не выяснять в момент инцидента.
Где именно заканчивается общий код: таблица по слоям приложения
На практике доля общего кода сильно зависит от слоя приложения, а не от проекта в целом. Ниже — ориентир, на какую экономию стоит рассчитывать, а где закладывать полноценную нативную разработку под каждую платформу.
| Слой приложения | Доля общего кода на практике | Типичная проблема | Что почти всегда остаётся платформенным |
|---|---|---|---|
| Бизнес-логика и валидация | Высокая | Конфликты версий Kotlin между модулями | Почти ничего |
| Сеть и кэширование (Ktor) | Высокая | Разное поведение TLS и certificate pinning на iOS и Android | Настройки безопасности сети |
| UI-слой (Compose Multiplatform / SwiftUI) | Низкая, сильно варьируется | Рассинхрон анимаций и деталей платформенного UX | Почти весь UI |
| Камера, биометрия, push, геолокация | Почти нулевая | Для каждого API нужен отдельный expect/actual на каждую ОС | Весь слой целиком |
| Хранение данных и шифрование под 152-ФЗ | Переменная | Требования к локализации персональных данных зависят от факта хранения на устройстве | Слой хранения и шифрования |
Как исправить архитектуру, если platform-specific логика уже смешалась с бизнес-кодом
Первый шаг — найти нарушения границы: поиск по commonMain на импорты UIKit, Android SDK или платформенных библиотек внутри shared-модуля почти всегда вскрывает десятки мест, где логика и платформенный код перепутаны. Дальше — разложить архитектуру по слоям: domain и data остаются в commonMain и ничего не знают о UI и системных API, вся платформенная специфика уходит в androidMain и iosMain через expect/actual только там, где это действительно необходимо.
Дальше нужны интеграционные тесты, которые реально гоняются на обеих платформах, а не только на JVM. И отдельный бюджет: команде из Kotlin-разработчиков без опыта Swift и нативного Android часто не хватает квалификации закрыть платформенный слой самостоятельно — на этом чаще всего и стопорятся сроки.
Если проект уже большой, не пытайтесь вынести всё в shared-модуль одним рывком. Начните с изолированной логики без побочных эффектов — валидации, расчётов, маппинга данных, — и только после того, как она стабильно работает на обеих платформах, переходите к более рискованным зонам вроде кэширования или синхронизации состояния.
Что делать, если ошибка в native-модуле повторяется после каждого обновления зависимостей
Частая причина — версии Kotlin, Compose Multiplatform, Xcode и CocoaPods обновляются несинхронно, а совместимость между ними жёстко зафиксирована в матрице конкретного релиза. Обновили один компонент без проверки матрицы — получили тот же баг заново, даже если формально его уже чинили на прошлой неделе. Отдельная головная боль — обновления Xcode: Apple выпускает их независимо от релизов Kotlin, и мажорное обновление среды разработки может сломать сборку iOS-таргета даже без единой строчки изменений в Kotlin-коде.
Дополнительно стоит держать матрицу реальных устройств для тестов: часть багов Kotlin/Native воспроизводится только на конкретных версиях iOS или на бюджетных Android-устройствах с урезанной памятью, а не в симуляторе или эмуляторе.
Что помогает: фиксировать версии через version catalog и обновлять их пакетом, а не по одной библиотеке; заводить регрессионный тест именно на этот баг на обеих платформах; не считать, что если баг исправлен на Android, то он исправлен и на iOS — у одинакового симптома часто разные первопричины на каждой платформе.
Как предотвратить разрастание платформенного кода в новом KMP-проекте
Профилактика дешевле лечения на этапе, когда команда только выбирает архитектуру. Опыт проектов, где общий код разросся не туда, обычно сводится к одному и тому же: границы никто не проговорил вслух до старта разработки. Стоит закрыть это заранее:
- ✓заранее зафиксировать, какие слои реально будут общими — бизнес-логика, сеть, модели данных, — а какие всегда останутся платформенными: UI, доступ к железу, уведомления;
- ✓не продавать заказчику «один код» как «ноль нативной разработки» — в бюджете и сроках сразу закладывать двух нативных специалистов, а не одного Kotlin-разработчика;
- ✓выбирать библиотеки с официальной поддержкой Multiplatform, а не community-обёртки с непонятным сроком жизни;
- ✓подключать оба таргета — iOS и Android — в CI с первого дня проекта, а не добавлять сборку под iOS позже.
Эти четыре пункта редко стоят дополнительных денег на старте — они меняют то, как считается бюджет, а не увеличивают его. Дороже обходится обратное: месяцы на переделку, когда platform-specific код обнаруживается уже в проде.
Когда мобильному приложению на KMP нужна интеграция с 1С и доработка backend
Большинство B2B-приложений для среднего и малого бизнеса в Москве — это не самостоятельный продукт, а тонкий клиент поверх учётной системы: склад, CRM, розница чаще всего работают через 1С. Здесь проявляется ещё одна граница, которую редко закладывают на старте: насколько бы аккуратно ни была построена архитектура shared-модуля, мобильное приложение сломается, если на стороне 1С меняется API при очередном обновлении.
Если мобильную часть и 1С-бэкенд ведут разные подрядчики, обновление на одной стороне почти никогда не согласовано с другой: 1С обновили в пятницу, а мобильная команда узнаёт об изменившемся API только когда приложение падает в бою. Когда обе части — под одним подрядчиком, изменения в 1С планируются с учётом мобильного клиента, а не выясняются постфактум.
Поэтому для таких проектов имеет смысл вести мобильную и учётную часть согласованно: доработка 1С формирует стабильный набор эндпоинтов под нужды приложения, а предсказуемое обновление 1С исключает ситуацию, когда мобильный клиент падает после планового патча учётной системы. Для розницы и склада это чаще всего означает интеграцию с 1С:Управление торговлей.
Отдельный момент — персональные данные пользователей приложения. Общий Kotlin-код не освобождает от требований 152-ФЗ: место и способ хранения персональных данных нужно продумывать на уровне платформенного слоя хранения, а не общей бизнес-логики. Если приложение метит в госзаказчиков или окологосударственные структуры, стоит заранее сверяться и с реестром российского ПО — требования к регистрации не зависят от того, сколько кода у вас общее.
Мы в ukved.ru разрабатываем мобильные приложения на Kotlin Multiplatform с чётко проведённой границей shared/native и, при необходимости, дорабатываем 1С-бэкенд под конкретные эндпоинты приложения. Разработка мобильного приложения — от 500 000 руб., сопровождение и доработка 1С-стороны — от 3 800 руб/час.
❓ Частые вопросы
Сколько кода реально можно сделать общим в Kotlin Multiplatform?
Зависит от типа приложения, но на практике общими получаются бизнес-логика, сетевой слой и модели данных — это ядро логики. UI, доступ к камере, биометрии, push-уведомлениям и хранилищу почти всегда остаются платформенными. Ориентируйтесь не на маркетинговые проценты, а на конкретные слои вашего приложения.
KMP заменяет отдельную разработку под iOS и Android?
Нет. KMP убирает дублирование в бизнес-логике и сети, но UI, интеграция с системными сервисами и публикация в сторах всё равно требуют нативных iOS- и Android-разработчиков. Планировать команду и бюджет стоит как для двух платформ с разными сроками релиза, а не как для одной.
Можно ли постепенно перевести существующее нативное приложение на KMP?
Да, это безопаснее, чем переписывать всё сразу. Начните с изолированной логики без побочных эффектов — валидации, расчётов, маппинга данных — и переносите её в общий модуль первой. UI, кэширование и интеграции с железом трогайте в последнюю очередь, когда базовый слой уже стабилен на обеих платформах.
Сколько стоит разработка мобильного приложения на KMP под задачи бизнеса?
Разработка мобильного приложения в ukved.ru начинается от 500 000 руб., итоговая стоимость зависит от количества платформ, сложности бизнес-логики и интеграций с внешними системами вроде 1С. Точную оценку с разбивкой по этапам даём после короткого брифа по задаче и целевой аудитории приложения.
Наше приложение должно синхронизироваться с 1С — это усложняет KMP-проект?
Да, добавляет отдельную зависимость: стабильность API на стороне 1С важна не меньше архитектуры мобильного клиента. Мы совмещаем доработку 1С-бэкенда с мобильной разработкой в одном контракте, чтобы контракт API не менялся неожиданно при плановых обновлениях учётной системы и не ломал приложение в проде.
Или позвоните: +7 495 133-92-44 — в рабочее время с 9:00 до 19:00
Остались вопросы? Нужна помощь?
Менеджеры компании с радостью ответят на ваши вопросы, произведут расчет стоимости услуг и подготовят индивидуальное коммерческое предложение.
Бесплатная консультация

