Привет, меня зовут Сергей, я Android Lead в компании Broniboy —  сервисе мультидоставки, который работает не только с ресторанами, но и с магазинами, аптеками и обычными людьми. Учитывая уровень конкуренции на этом рынке, каждый клиент на счету. Достаточно одной ошибки, чтобы потерять его, поэтому нам нужен надёжный канал поддержки пользователей. Наш выбор пал на чат, простой и понятный пользователю канал связи. Он позволяет не только оперативно реагировать на жалобы, но и привности частичку «человечности» в общение с техподдержкой. Изучив готовые SDK и взвесив все «за» и «против», мы решили написать собственный SDK чата. Одна из основных идей — переиспользование кода между платформами, а сегодня это Android, iOS и Web. Поэтому, для MVP мы решили попробовать Kotlin Multiplatform. Изучив несколько примеров и библиотек на просторах Интернета, выбрали такой стек технологий: Ktor и Kotlin Serialization для сети; SQDelight для хранения; Koin для DI; JetBrain Compose, MVIKotlin и Decompose в качестве каркаса архитектуры приложения; и Coroutines для связи всего этого воедино. Но всё же собрать это в единую структуру, несмотря на небольшой выбор библиотек, поддерживающих мультиплатформенность, оказалось нетривиальной задачей. Из-за отсутствия опыта и малого количества актуальной информации пришлось бы потратить много времени. Поэтому мы решили начать с простых и понятных крайних слоёв, и уже потом, имея готовые интерфейсы, связать их воедино. Data-слой оказался довольно простым: Ktor и Coroutines имеют исчерпывающую документацию, SQDelight напоминает Room, а Koin ничем не отличается от Android-варианта. Конечно, при использовании Ktor ощущается нехватка Retrofit’а с его аннотациями, но их успешно заменяют DSL-сборщики. А поддержка OkHttpClient позволяет переиспользовать клиент из основного приложения. С SQDelight всё ещё проще: это Room, который генерирует все классы за нас, достаточно лишь указать схему. С Koin ситуация немного отличается, так как мы хотим инициализировать его не глобально, а только на время работы с чатом. К счастью, для этого в Koin есть механизм Context Isolation, который позволяет изолировать зависимости SDK, создавая и удаляя их при необходимости. Переходим к другому концу нашей архитектуры — UI. Хотя мы смогли легко реализовать простой интерфейс , добиться результата «как в макете» оказалось не так просто. Поле ввода сообщения. Ушло много времени на то, чтобы сделать его определённой высоты и выровнять по центру, не уменьшая при этом кликабельнуюй зону. Ссылки в тексте. В Compose нет autolink’а, поэтому пришлось использовать обычный TextView. BottomSheet. Изменение состояния BottomSheet через методы: Объединив получившиеся слои, мы проверили, что всё корректно работает. После этого осталось реализовать слой бизнес-логики, связав всё воедино. Имея интерфейсы с обеих сторон и опробовав разные подходы, мы реализовали логичную архитектуру, подходящую для наших экранов: Composable, который отображает состояние экрана и осуществляет навигацию: Component, который реагирует на изменение состояния экрана: Store, который загружает и обрабатывает информацию: И на этом всё, структура приложения готова! Мы считаем эксперимент успешным. Kotlin Multiplatform действительно позволяет переиспользовать значительную часть логики. При этом всегда можно обойти проблемные места с помощью expect/actual. Это добавляет гибкости в использовании общего модуля среди разных платформ. Благодаря мультиплатформенности, нам удалось ощутимо сократить затраты на разработку SDK. Для каждой платформы нам необходимо реализовать только UI-слой, а бизнес-логика будет переиспользована из общего модуля. Более того, благодаря этому чат на всех платформах работает единообразно. Android Lead в Broniboy