«Я вас видел». Приложение, которое помнит, с кем вы встречались на конференциях и выставках
Однажды к нам пришли, чтобы создать достойную конкуренцию LinkedIn, «Сетке» и TenChat. Мы взялись за дело и написали приложение для конференций, которое находит контакты по геопозиции. Рассказываем, как воплотили в код идею смелого стартапа, преодолевая технические сложности.
История проекта
К нам обратился московский предприниматель, уставший терять полезные знакомства на торговых выставках. Так родилось решение: приложение "I saw you", которое автоматически запоминает людей вокруг вас.
Достаточно просто иметь установленное приложение на конференции — и оно автоматически сохранит «встречи» с другими участниками. Позже можно просмотреть их профили и написать, минуя привычный обмен контактами.
Мы сделали продукт специально для ивент-индустрии: организаторы могут размещать QR-коды или рекомендовать приложение участникам. А написали его на Kotlin Multiplatform, чтобы сразу охватить обе мобильные платформы с нативным интерфейсом.
Как работает «I saw you»
Приложение через GPS определяет, кто находился рядом с вами в радиусе до 50 метров. Если оба человека хотя бы раз открыли приложение во время мероприятия, они автоматически попадают друг другу в список «Виделись».
После события вы можете открыть приложение и увидеть, с кем пересекались — даже если не успели пообщаться лично.
Пользователь заполняет свой профиль: загружает фото, указывает чем занимается, из какой компании, кого ищет и чем может быть полезен. Механика здесь похожа на дейтинг-приложения, но цель — не свидания, а деловые знакомства.
Со временем в приложении появился и поиск вне событий: можно искать людей по хештегам, отраслям, имени или городу. Например, вы приехали в другой регион или страну — и хотите найти потенциальных партнеров, поставщиков или коллег. Просто вбиваете нужное слово — и видите тех, кто вам нужен..
Предусмотрели защиту от навязчивости
В приложении предусмотрена защита от лишнего внимания. Написать сообщение можно любому, но личный номер телефона остается скрытым — он не раскроется, пока пользователь сам этого не захочет. Все общение происходит внутри приложения, через встроенные чаты.
Так случайное соседство на конференции превращается в продуманный, аккуратный деловой контакт — без визиток, поиска в LinkedIn и неловких уточнений «а вы кто?».
Определились, что работать будем на Kotlin Multiplatform
Перед нами стоял выбор: делать приложение отдельно под iOS и Android, использовать кроссплатформу вроде Flutter или писать на Kotlin Multiplatform (KMP). Мы выбрали последнее, и вот почему.
KMP дает баланс между эффективностью и UX. Бизнес-логику — сеть, хранение, навигацию пишем один раз, а интерфейсы остаются нативными: под iOS — SwiftUI, под Android — Jetpack Compose. Это дает пользователю привычный опыт, а команда меньше занимается дублированием.
Во-вторых заказчику сразу требовался готовый продукт, который можно публиковать в магазинах приложений, демонстрировать организаторам и дальше совершенствовать. KMP идеально подходит для этого.
В-третьих, мы смотрели в перспективу. Если появятся web- или desktop-версии, KMP позволит расширяться без переписывания всего с нуля — через Compose Multiplatform.
Впрочем, несмотря на опыт, проект «I saw you» не дал нам пройтись по прямой. Почти каждый функциональный блок требовал донастройки, тестов, обходных путей и адаптации под реальность. Ниже рассказываем, с чем столкнулись.
Невидимые сложности геолокации
Центральная функция приложения — определить, кто находился рядом с пользователем на мероприятии. В теории задача простая: получить GPS-координаты и зафиксировать пересечения.
Однако разные устройства обрабатывают геолокацию по-разному. Где-то координаты приходят быстро, где-то GPS включается с задержкой, а в некоторых случаях нужно запрашивать сразу несколько разрешений — в строго определенной последовательности.
Особенно непредсказуемо вели себя устройства Samsung, Xiaomi и старые модели Android.
Ситуация усложнилась, когда начались проблемы с GPS — координаты стали определяться неточно. Чтобы не зависеть только от геолокации, планировалось добавить поддержку QR-кодов и другие способы подтверждения контактов. Например рассматривали не самый очевидный способ — сравнение фоновых шумов.
К счастью, проблемы с геолокацией вскоре устранили, и эту функцию внедрять не пришлось.
Написали чат на KMP с нуля
Когда мы начинали, в Kotlin Multiplatform не было готовых решений для чатов. Ни библиотек, ни шаблонов — все пришлось собирать вручную: продумывать архитектуру, настраивать WebSocket-соединения и добиваться стабильной работы, даже если приложение свернуто или работает в фоне.
Мы реализовали полноценный real-time чат с:
- WebSocket-подключением и синхронизацией сообщений в реальном времени;
- push-уведомлениями, которые приходят даже при закрытом приложении;
статусами online/offline, реакциями, прочтением, пагинацией;
отложенной отправкой сообщений при потере связи;
очередью сообщений, чтобы они не терялись и приходили в нужном порядке;
навигацией между экранами и открытием чатов по push-уведомлению;
логикой переподключения: если соединение теряется, оно восстанавливается автоматически, по умному алгоритму.
Это было непросто, поскольку Android и iOS по-разному работают с фоновыми задачами, сокетами и переходами между экранами. Часто мы просто пробовали разные подходы, потому что документации по чатам на KMP не было.
Зато теперь у нас есть свое стабильное решение для чатов на KMP — и это стало одной из самых мощных частей проекта. Наш проджект-менеджер Саша как-то даже написала об этом целую статью на Хабре — для разработчиков. Заходите почитать.
Офтоп. Почему отсутствие готовых решений — это вызов
Когда под задачу есть готовые библиотеки и гайды, разработка идет быстрее. Можно сфокусироваться на продукте, подключить проверенные модули, собрать интерфейс, не тратя время на базовые вещи.
Но если таких решений нет — начинается другая история. Ты не просто пишешь код. Ты сам придумываешь, как это должно работать, хотя никто до тебя это не делал.
После проверяешь, почему ничего не работает. И только потом, через тесты, костыли, переработки и десятки сценариев собираешь все заново, чтобы все работало стабильно — на разных моделях, в разных странах, с отключенным GPS, плохим сигналом и всеми разрешениями, которые пользователь случайно не выдал.
Но в этом и преимущество: рабочее решение, прошедшее реальные испытания — полностью твое, и ты знаешь о нем больше других.
Архитектура «на вырост»
С самого начала мы проектировали систему так, чтобы она не уперлась в потолок при первых же успехах. Заказчик рассчитывал на рост, а значит нужна была инфраструктура, которая выдержит пиковые нагрузки, всплески регистраций и тысячи одновременных соединений.
Мы выбрали микросервисную архитектуру. И каждую зону ответственности — авторизацию, поиск, чаты, хранение профилей — вынесли в отдельные сервисы. Это дало нам гибкость в масштабировании: если начнет «проседать» конкретный модуль, его можно масштабировать независимо от остальных, без перекройки всей системы.
Для поиска внутри приложения мы интегрировали ElasticSearch: по имени, хештегу, отрасли, городу — все отрабатывает быстро, даже при больших объемах данных. Под капотом настроили индексацию и фильтрацию так, чтобы запросы выполнялись почти мгновенно.
Все модули проектировались не «впритык», а с запасом — мы сразу закладывали поддержку горизонтального масштабирования, очередей сообщений и отказоустойчивости. Это означает, что при росте пользовательской базы приложение не начнет тормозить, терять данные или рушиться.
Про сложности
Для мгновенной передачи сообщений обычные HTTP-запросы не подходят — нужно постоянное двустороннее соединение. Поэтому мы использовали WebSocket — протокол, который требует ручной настройки на каждой платформе. Соединение должно жить даже при переходе между экранами, переключении интернета, сворачивании приложения — и тут начались настоящие сложности.
Мы переписывали очередь сообщений, настраивали логику переподключений, обрабатывали edge-кейсы, когда приложение закрыто, но пуш все равно должен дойти, а потом корректно открыть нужный экран.
Compose и SwiftUI тоже не облегчали задачу. Например, навигация в Compose оказалась сырой: баги, нестабильности, неожиданные конфликты.
Простая, казалось бы, фича — открыть чат с пуша — требовала десятков сценариев, условий и тестов. Поддержка KMP для пагинации отсутствовала — поэтому мы писали свою реализацию с нуля. А стабильность сокетов обеспечивали через кастомные retry-алгоритмы, слежение за соединением и приоритизацию событий.
Добавим сюда отсутствие готовых библиотек под KMP для мессенджеров (на GitHub решений почти не было), нестабильность SDK, разные баги и ограничения между платформами — и получаем настоящий инженерный челлендж.
Многое работало… почти. Сообщения «почти» доходили, экраны «почти» открывались по пушу. Но чтобы убрать эти «почти» и довести продукт до стабильной боевой версии, ушли недели кропотливой работы.
Вместо итогов
Kotlin Multiplatform дал нам лучшее из двух миров: общую логику для обеих платформ и нативные интерфейсы — при минимуме костылей.
На сегодняшний день:
- приложение опубликовано в RuStore;
команда завершила базовые доработки после запуска;
следующий шаг — выход в Google Play и App Store;
затем — тестирование продукта на реальных конференциях.
Проект еще в пути, аудитория только начинает формироваться. Но уже сейчас мы видим: своя ниша у него есть. Мы сделали надежную основу, в том числе — одно из первых в индустрии решений для чатов на Kotlin Multiplatform. И теперь готовы развивать продукт дальше: вместе с клиентом, сообществом и новыми задачами.
Если вы думаете о запуске MVP или полноценного продукта — приходите, обсудим. Мы поможем определить: действительно ли вам нужно большое решение сразу, или можно начать с более простого, чтобы быстро проверить гипотезы.
Проконсультируем, покажем похожие кейсы — и соберем то, что нужно. Пишите в личку https://t.me/john081076
Читайте другие кейсы на нашем сайте https://softorium.pro/, подписывайтесь на наш канал https://t.me/+0goGvsMB7wRiYmIy чтобы лучше понять мир ИТ изнутри.