Implementing Smartwatch Data Synchronization with Mobile Applications
Apple Watch and Wear OS are fundamentally different synchronization ecosystems. Apple has WatchConnectivity framework with strict transmission limits. Google has Data Layer API over BLE stack. Cross-platform React Native or Flutter don't help here: smartwatch sync requires native code in Swift/WatchKit and Kotlin/Wearable Data Layer.
Apple Watch: WatchConnectivity
WCSession is the only channel between iPhone app and Watch App. Three transmission methods with different semantics:
| Method | Delivery | Size | Background | Scenario |
|---|---|---|---|---|
sendMessage |
Immediate | < 64 KB | Only when watch reachable | Real-time commands |
transferUserInfo |
FIFO queue | Small dict | Yes, first opportunity | Settings, config |
transferFile |
Background | Up to MB | Yes | Tracks, audio, large data |
// iPhone → Watch: urgent command
class PhoneSessionManager: NSObject, WCSessionDelegate {
func sendWorkoutCommand(_ command: WorkoutCommand) {
guard WCSession.default.isReachable else {
// Watch unreachable — queue via 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 fails if watch died or out of range
self.queueCommandForLater(command)
}
)
}
}
Critical: sendMessage works only if WCSession.default.isReachable == true. This means watch is not just in BLE range but its app is active or backgrounded with permission. If user closed Watch App — isReachable returns false even when watch is nearby.
Workout Data Synchronization: Watch → iPhone
After workout, Watch App collects data (heart rate, cadence, GPS track, segments) and transfers to iPhone:
// Watch App — send after workout completion
func finishWorkout(_ session: HKWorkoutSession) {
let workoutData = WorkoutSummary(
duration: session.currentActivity.duration,
heartRateSamples: collectedHRSamples,
route: collectedLocations,
)
guard let encoded = try? JSONEncoder().encode(workoutData) else { return }
// For large data (10k-point GPS track) — 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()])
}
}
On iPhone receive via session(_:didReceiveFile:) or session(_:didReceiveUserInfo:). Move file from session documentDirectory before exiting delegate method — otherwise iOS deletes it.
Wear OS: Wearable Data Layer API
Android side — DataClient, MessageClient, ChannelClient from com.google.android.gms:play-services-wearable.
// Send data from watch to phone via 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)
}
}
}
}
}
}
// On watch — write 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() — deliver without delay
.await()
}
DataItem replicates automatically — no need to monitor connection state. Wear OS syncs itself when watch connects to phone.
HealthKit: Reading Workout Data on iPhone
Workout data recorded by Watch App via HealthKit is accessible to iPhone app directly — without 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
// process
}
healthStore.execute(query)
}
For GPS track of workout — HKWorkoutRoute via HKWorkoutRouteQuery. Query separately after getting HKWorkout — route stored as linked object.
Implementing smartwatch data sync (Apple Watch + Wear OS) with mobile app: 4–6 weeks. Cost calculated individually after analyzing required data types and platforms.







