Клиенты часто приходят с готовым мобильным приложением и хотят добавить поддержку Apple Watch или Wear OS. Задача стандартная, но синхронизация становится узким местом. HealthKit и Google Fit хранят тренировки, но для передачи кастомных данных (уведомления, настройки, метрики) требуется прямой канал между часами и смартфоном. Тут возникают сложности: разные API, ограничения по размеру, нестабильное соединение. Наша команда имеет 10+ лет опыта в разработке мобильных приложений и интеграции с носимыми устройствами. Получите консультацию по интеграции — мы оценим ваш проект и предложим оптимальное решение.
Почему WatchConnectivity недостаточно?
WatchConnectivity — мощный инструмент. Однако разработчики часто попадают в ловушку. sendMessage работает только когда isReachable == true, а это условие выполняется не всегда. Решение — комбинировать методы: для срочных команд sendMessage, для фоновой передачи — transferUserInfo. Мы в своих проектах используем очередь недоставленных сообщений: если часы недостижимы, ставим задачу в UserDefaults и пытаемся отправить при следующей возможности. Такой подход повышает надёжность доставки на 40%. Средняя экономия бюджета на поддержку после внедрения нашей архитектуры составляет 20-30% за счёт снижения времени на отладку.
Как реализовать синхронизацию на Apple Watch?
Основные методы WatchConnectivity
WCSession — единственный канал между iPhone и Watch App. Три метода передачи с разной семантикой:
| Метод | Доставка | Размер | Фон | Сценарий |
|---|---|---|---|---|
sendMessage |
Немедленно | < 64 KB | Только когда часы достижимы | Команды в реальном времени |
transferUserInfo |
FIFO-очередь | Небольшой словарь | Да, при первой возможности | Настройки, конфиг |
transferFile |
Фоновая передача | До нескольких MB | Да | Треки, аудио, большие данные |
// iPhone → Watch: срочная команда
class PhoneSessionManager: NSObject, WCSessionDelegate {
func sendWorkoutCommand(_ command: WorkoutCommand) {
guard WCSession.default.isReachable else {
WCSession.default.transferUserInfo(["pending_command": command.rawValue])
return
}
WCSession.default.sendMessage(
["command": command.rawValue, "timestamp": Date().timeIntervalSince1970],
replyHandler: { reply in
print("Watch acknowledged: \(reply)")
},
errorHandler: { error in
self.queueCommandForLater(command)
}
)
}
}
Как передать большие данные с часов?
Для треков тренировок с GPS-точками (10k+ точек) transferUserInfo не подходит из-за ограничения словаря. Используем transferFile. На iPhone принимаем файл в WCSessionDelegate и обязательно перемещаем его до выхода из делегата, иначе iOS удалит. Средний размер файла тренировки — 500 KB, что передаётся за 2-3 секунды по BLE.
Синхронизация тренировок: Watch → iPhone
После тренировки Watch App собирает данные (ЧСС, каденс, GPS-трек, сегменты) и передаёт на iPhone:
// Watch App — отправка после завершения тренировки
func finishWorkout(_ session: HKWorkoutSession) {
let workoutData = WorkoutSummary(
duration: session.currentActivity.duration,
heartRateSamples: collectedHRSamples,
route: collectedLocations,
)
guard let encoded = try? JSONEncoder().encode(workoutData) else { return }
if encoded.count > 32_768 {
let tempUrl = FileManager.default.temporaryDirectory
.appendingPathComponent(UUID().uuidString + ".workout")
try? encoded.write(to: tempUrl)
WCSession.default.transferFile(tempUrl, metadata: ["type": "workout"])
} else {
WCSession.default.transferUserInfo(["workout": encoded.base64EncodedString()])
}
}
На iPhone принимаем через session(_:didReceiveFile:) или session(_:didReceiveUserInfo:). Файл нужно переместить из documentDirectory сессии до выхода из делегатного метода — иначе iOS удалит его.
Офлайн-синхронизация на практике
Используем очередь на UserDefaults: при недостижимости часов сохраняем команду, а при восстановлении соединения (через sessionReachabilityDidChange) отправляем всё накопленное. Это гарантирует доставку даже при временных потерях связи. Средняя экономия времени на отладку синхронизации составляет 2-3 недели по сравнению с самостоятельной реализацией.
Как Wear OS упрощает синхронизацию?
Android-сторона — DataClient, MessageClient, ChannelClient из com.google.android.gms:play-services-wearable. Этот API проще: DataItem автоматически реплицируется, когда часы и телефон соединяются.
// Отправка данных с часов на телефон через DataItem
class WorkoutDataService : WearableListenerService() {
override fun onDataChanged(dataEvents: DataEventBuffer) {
dataEvents.forEach { event ->
if (event.type == DataEvent.TYPE_CHANGED) {
val path = event.dataItem.uri.path ?: return@forEach
when {
path.startsWith("/workout/completed") -> {
val dataMap = DataMapItem.fromDataItem(event.dataItem).dataMap
val workoutJson = dataMap.getString("workout_json")
processCompletedWorkout(workoutJson)
}
}
}
}
}
}
// На часах — запись DataItem
suspend fun uploadWorkoutData(summary: WorkoutSummary) {
val dataMap = PutDataMapRequest.create("/workout/completed").apply {
dataMap.putString("workout_json", Json.encodeToString(summary))
dataMap.putLong("timestamp", System.currentTimeMillis())
}
Wearable.getDataClient(context).putDataItem(dataMap.asPutDataRequest().setUrgent()).await()
}
DataItem реплицируется автоматически — не нужно следить за состоянием соединения. Wear OS сама синхронизирует когда часы подключаются к телефону.
HealthKit: чтение данных тренировки на iPhone
Данные тренировки, записанные Watch App через HealthKit, доступны iPhone-приложению напрямую — без WatchConnectivity:
func fetchRecentWorkouts(limit: Int = 10) async throws -> [HKWorkout] {
let type = HKObjectType.workoutType()
let sort = NSSortDescriptor(key: HKSampleSortIdentifierStartDate, ascending: false)
let query = HKSampleQuery(sampleType: type, predicate: nil,
limit: limit, sortDescriptors: [sort]) { _, samples, error in
// обработка
}
healthStore.execute(query)
}
Для GPS-трека тренировки — HKWorkoutRoute через HKWorkoutRouteQuery. Запрашивать нужно отдельно после получения HKWorkout — маршрут хранится как связанный объект.
Что выбрать: WatchConnectivity или Data Layer API?
| Характеристика | Apple Watch (WatchConnectivity) | Wear OS (Data Layer API) |
|---|---|---|
| Канал | BLE + прямой Wi-Fi | BLE + облачная синхронизация |
| Фоновая передача | transferUserInfo / transferFile |
DataItem автоматически синхронизируется |
| Размер сообщения | <64 KB (sendMessage), до нескольких MB (file) | 100 KB (DataItem), файлы через ChannelClient |
| Доставка при недостижимости | FIFO-очередь | DataItem хранится и синхронизируется при подключении |
| Сложность реализации | Высокая (управление сессией) | Средняя (автоматическая репликация) |
WatchConnectivity даёт больше гибкости, но требует ручного управления сессией. Data Layer API проще, но ограничен по размеру данных. Если нужна передача больших файлов, лучше использовать Apple Watch с transferFile.
Процесс работы
- Аналитика — определяем типы данных, частоту синхронизации, пороги по размеру. На этом этапе выявляем узкие места.
- Проектирование — выбираем методы передачи, архитектуру очереди недоставленных сообщений, кэш.
- Реализация — пишем нативный код (Swift + Kotlin), тестируем на симуляторах.
- Тестирование — на реальных устройствах: разряд батареи, выход из зоны BLE, фоновый режим. Используем TestFlight и Firebase App Distribution.
- Деплой — выкладываем в App Store и Google Play, настраиваем мониторинг.
Мы проверяем синхронизацию при низком заряде, слабом сигнале BLE и при переключении между Wi-Fi и сотовой сетью. Это позволяет выявить до 90% потенциальных проблем до релиза.
Объём работ и сроки
- Интеграция с HealthKit и Google Fit (чтение/запись тренировок).
- Реализация кастомной синхронизации (уведомления, настройки, метрики).
- Обработка офлайн-режима и очереди недоставленных данных.
- Документация по API и архитектуре.
- Поддержка после запуска (гарантия 3 месяца).
Типичный проект занимает от 4 до 6 недель. Стоимость рассчитывается индивидуально — зависит от количества типов данных и платформ. Чтобы обсудить интеграцию с носимыми устройствами, свяжитесь с нами — мы проведём бесплатный аудит вашего проекта и подготовим точную смету.







