Реализация синхронизации данных умных часов с мобильным приложением
Apple Watch и Wear OS — две принципиально разные экосистемы синхронизации. У Apple — WatchConnectivity framework с жёсткими ограничениями на передачу. У Google — Data Layer API поверх BLE-стека. Кросс-платформенный React Native или Flutter здесь не поможет: синхронизация с часами требует нативного кода на Swift/WatchKit и Kotlin/Wearable Data Layer.
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 {
// Часы недостижимы — ставим в очередь через transferUserInfo
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
// sendMessage упадёт если часы разрядились или вышли из радиуса
self.queueCommandForLater(command)
}
)
}
}
Важное ограничение: sendMessage работает только если WCSession.default.isReachable == true. Это значит часы не только в зоне BLE, но и их приложение активно или в фоне с разрешением. Если пользователь закрыл Watch App — isReachable вернёт false даже когда часы рядом.
Синхронизация тренировочных данных: 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 }
// Для больших данных (GPS-трек 10k точек) — transferFile
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 удалит его.
Wear OS: Wearable Data Layer API
Android-сторона — DataClient, MessageClient, ChannelClient из com.google.android.gms:play-services-wearable.
// Отправка данных с часов на телефон через 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()) // 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 — маршрут хранится как связанный объект.
Реализация синхронизации данных умных часов (Apple Watch + Wear OS) с мобильным приложением: 4–6 недель. Стоимость рассчитывается индивидуально после анализа требуемых типов данных и платформ.







