Синхронізація даних між мобільним додатком та умним годинником
Синхронізація між телефоном та годинником — це не просто «відправити JSON туди-обратно». Bluetooth-з'єднання переривається, користувач йде в душ з годинником без телефону, додаток на телефоні вбивається системою. Дані мають прийти в потрібному порядку, без дублів та без втрат — та при цьому не сжирати батарею ні на одній з сторін.
Два різних світи: Wear OS та watchOS
На Wear OS — Wearable Data Layer API. Три канали для різних задач:
| Канал | Розмір | Гарантія доставки | Коли використовувати |
|---|---|---|---|
| DataClient | до 100 КБ | Так (при підключенні) | Конфігурація, налаштування, невеликі дані |
| MessageClient | будь-який | Ні | Команди реального часу |
| ChannelClient | без обмежень | Так | Файли, медіа, великі датасети |
На watchOS — WatchConnectivity framework. WCSession.transferUserInfo() для фонової доставки, sendMessage() для інтерактивного обміну (лише коли обидва пристрої доступні), transferFile() для файлів.
Помилка, яку роблять всі: використовувати sendMessage() там, де потрібен transferUserInfo(). sendMessage вимагає активного з'єднання. Якщо годинник в airplane mode — дані втрачаються. transferUserInfo ставить дані в чергу та доставляє при наступному підключенні.
Conflict Resolution та Idempotency
Уявіть: користувач редагує замітку на телефоні та на годинику одночасно (в режимі офлайн). Обидва пристрої потім синхронізуються. Чиї дані правильні?
Без стратегії разрешения конфліктів — останній переможе. Це погано для будь-яких даних крім «показань датчиків».
Будуємо синхронізацію з vector clock або спрощеним last-write-wins з timestamp. Кожна зміна отримує updated_at в мілісекундах UTC та device_id. При конфлікті — явна політика: для користувацьких даних спитуємо, для телеметрії — беремо з більшим timestamp.
Idempotency обов'язкова: повторна доставка того ж DataItem не має створити дубль у базі. На Wear OS DataClient сам дедуплицирует по шляху (/data/workouts/123) — якщо дані не змінились, onDataChanged не викличається.
Робота у фоні
На Android часове додаток отримує дані через WearableListenerService — він запускається системою навіть якщо додаток не активний. Але з Android 13+ та Wear OS 4 додалися обмеження на запуск сервісів. WearableListenerService — винятак, він продовжує працювати. Однак якщо в нім робити важкі операції (запис у Room, мережеві запити), потрібен coroutine scope, привязаний до життєвого циклу сервіса.
На iOS WCSession працює через application(_:didReceiveUserInfo:) делегат — він викликається навіть при фоновій доставці. Але оновлювати UI напрямо не можна — лише через DispatchQueue.main.async.
Батарейна оптимізація. Кожна синхронізація — це Bluetooth wake-up. Групуємо дрібні оновлення через батчинг: замість відправки кожного кроку окремо — агрегуємо за 30 секунд та відправляємо одним DataItem. На Wear OS використовуємо PassiveMonitoringClient для збору даних здоров'я без постійного wake lock.
Практичний приклад
Додаток для трекингу тренувань: годинник збирає ЧСС кожну секунду, телефон зберігає історичні дані та показує графіки.
Рішення: на годинику HealthServicesClient → ExerciseClient.prepareExercise() → агрегація кожні 5 секунд → MessageClient.sendMessage("/hr-batch", data). На телефоні WearableListenerService приймає батч → парсит → WorkManager записує в Room → Flow оновлює UI.
При розриві з'єднання годинник буферизує дані локально (Room на годинику, до 10 МБ). При відновленні — ChannelClient передає накопилене.
Сроки
Базова двусторонняя синхронізація (конфігурація + невеликі дані): 1–2 тижні. Повна система з офлайн-буфером, conflict resolution та health data: 3–5 тижнів. Вартість залежить від обсягу даних та вимог до надійності доставки.







