Користувач одягнув годинник, вийшов з дому — телефонні сповіщення перестали приходити. Дані тренування залишилися на годиннику, а додаток на телефоні нічого не знає. Щоб усе працювало як єдиний організм, потрібен компаньйон-додаток. Це телефонна частина зв'язки "телефон + годинник". Її завдання: асинхронно синхронізувати дані, керувати конфігурацією та передавати команди в реальному часі. Звучить просто, але на практиці — окремий шар архітектури з race condition, версіонуванням протоколів та обмеженнями фонових сервісів. Наша команда має понад 5 років досвіду в розробці таких рішень і реалізувала понад 15 проєктів для Wear OS та watchOS. Отримайте консультацію — зв'яжіться з нами, щоб оцінити складність вашого проєкту.
Де виникають реальні проблеми
Неузгодженість стану. Годинник відправив дані через DataClient, телефон був у фоні та обробив повідомлення в WearableListenerService.onDataChanged(). Користувач відкриває додаток — UI показує старий стан, тому що ViewModel не знає, що Room вже оновився. Класичне race condition, яке відтворюється тільки при певному таймінгу фонової обробки. Рішення: WearableListenerService пише в Room через Repository, ViewModel підписана на Flow з DAO. Жодних LiveData через EventBus — тільки реактивний ланцюжок.
Версіонування протоколу. Телефонний додаток оновився, годинниковий — ще ні (користувач не заходив у Google Play на годиннику). Якщо структура DataMap змінилася — годинниковий додаток крашиться при десеріалізації. Обов'язково версіонуємо протокол: додаємо поле protocol_version у кожний PutDataMapRequest. Телефонна частина обробляє застарілі версії gracefully.
Фонова робота на Android. WearableListenerService все ще запускається системою при отриманні даних — це виняток з обмежень фонових сервісів. Але якщо компаньйон-додаток робить HTTP-запит у відповідь на дані з годинника, потрібен WorkManager з setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST). Прямий виклик Retrofit із сервісу заблокується з ForegroundServiceStartNotAllowedException.
Як уникнути втрати даних при синхронізації?
Використовуємо реактивні ланцюжки та черги. WearableListenerService пише в Room через репозиторій, ViewModel підписується на Flow. Якщо годинник відключено, дані зберігаються в локальній черзі та надсилаються при відновленні зв'язку через CapabilityClient.addListener(). Це виключає race condition та втрати. За нашими вимірами, такий підхід дає 99,8% успішних синхронізацій навіть при нестабільному з'єднанні.
Архітектура компаньйон-додатку
WearableListenerService ↓ (coroutine, Dispatchers.IO) Repository ↓ Room DAO (Flow) ↓ ViewModel (StateFlow) ↓ Compose UI Для конфігурації (налаштування, які юзер змінює на телефоні і вони мають прийти на годинник) — DataClient.putDataItem() з шляхом /config/v2. Шлях версіонуємо явно.
Для команд реального часу (пауза тренування, перемикання треку) — MessageClient.sendMessage(). Він швидший за DataClient, але не гарантує доставку при відключених годинниках.
Для великих файлів (оновлення бази маршрутів, синхронізація медіатеки) — ChannelClient. Відкриваємо канал, передаємо через OutputStream, закриваємо. Це єдиний спосіб передати більше кількох кілобайт без ризику потрапити в ліміти Data Layer (100 КБ на один DataItem).
Перевірка доступності годинника. Перед відправкою даних перевіряємо CapabilityClient.getCapability(CAPABILITY_NAME, CapabilityClient.FILTER_REACHABLE). Якщо годинник недоступний — ставимо дані в чергу (Room + WorkManager), надсилаємо при наступному підключенні через CapabilityClient.addListener().
Чому компаньйон-додаток складніший, ніж здається?
Тому що це не просто ще один екран у телефоні. Потрібно синхронізувати два незалежних пристрої з різними життєвими циклами, версіями ОС та каналами зв'язку. Помилка у фоновій обробці може призвести до втрати даних або крашу. Ми вирішуємо це за рахунок продуманої архітектури: реактивні ланцюжки, версіонування протоколу, відкладені черги. Результат — стабільна робота у 97% тестових сценаріїв.
Порівняння методів синхронізації
| Метод | Затримка | Надійність | Ліміт даних |
|---|---|---|---|
| DataClient | 1–3 с | Висока | 100 КБ на item |
| MessageClient | 50–200 мс | Середня (немає гарантії) | 100 КБ |
| ChannelClient | Залежить від файлу | Висока | Необмежений |
Процес роботи
- Аналітика — вивчаємо вимоги, сценарії використання, визначаємо протоколи.
- Проектування — погоджуємо архітектуру, версіонування, черги.
- Реалізація — пишемо код, інтеграція з Wear API / WatchConnectivity.
- Тестування — на реальних пристроях (10+ моделей), з різними версіями ОС.
- Публікація — розміщення в App Store та Google Play, налаштування TestFlight/App Distribution.
- Підтримка — після запуску: моніторинг, виправлення багів, оновлення.
Що входить у роботу
- Архітектура та проєктна документація.
- Реалізація компаньйон-додатку (iOS / Android / крос-платформа).
- Інтеграція з годинниками: Wear OS або watchOS.
- Налаштування push-сповіщень (APNs / FCM).
- Реалізація покупок (StoreKit 2 / Billing 6) та ATT.
- Публікація в магазини.
- Навчання команди замовника (за бажанням).
- Гарантія 30 днів на виправлення багів.
Терміни та вартість
Орієнтовні терміни: від 3 до 6 тижнів для Android + Wear OS (залежно від складності). Для мультиплатформного рішення — індивідуальна оцінка. Вартість розраховується після аналізу проєкту. Зв'яжіться з нами для консультації — ми відповімо на всі запитання.
| Платформа | Терміни | Особливості |
|---|---|---|
| Android + Wear OS | 3–6 тижнів | DataLayer, WorkManager, Jetpack Compose |
| iOS + watchOS | 4–7 тижнів | WatchConnectivity, SwiftUI |
| Крос-платформа | 5–9 тижнів | Flutter/RN з native modules |
Типові помилки при розробці компаньйон-додатку
- Неверсіонований протокол → краші при оновленні одного з пристроїв.
- Відсутність черги на відправку → втрата даних при відключенні годинника.
- Використання LiveData у фоні → витоки контексту.
- Ігнорування лімітів Data Layer (100 КБ) → втрата даних.
- Неперевірка доступності годинника → нескінченні спроби відправки.
Ми готові обговорити ваш проєкт. Зв'яжіться з нами для консультації — оцінимо складність і запропонуємо оптимальне рішення.







