Building an Apple Watch or Wear OS Companion App

For a fitness client's Apple Watch companion, the initial temptation from the product team was to mirror most of the phone app's feature set — history, social feed, settings — onto the watch. We pushed back and scoped the watch app to three glanceable actions: start a workout, view live heart rate and pace, and end a workout, because usability testing showed anything requiring more than two taps or more than a few seconds of reading on the watch's small screen simply wasn't getting used in real workout conditions.
Connectivity between watch and phone needed explicit handling rather than an assumption of a live connection. We used WatchConnectivity's application context and transferUserInfo APIs for background-safe, queued data sync rather than the interactive messaging API for anything that could tolerate a delay, since the watch frequently runs standalone, out of Bluetooth range from the phone during an outdoor run, and messages sent via the interactive API when unreachable just fail silently rather than queuing.
Battery impact drove several architecture decisions. Continuous heart-rate sampling via HKWorkoutSession keeps the watch's sensor active and is the single biggest battery draw available to a third-party app; we scoped it strictly to the duration of an active workout session, since a bug in an early build left the sensor running after a workout ended and drained a full charge in about four hours during testing.
On Wear OS, Tiles — glanceable, swipeable widget-like surfaces on the watch face carousel — turned out to be a better fit than a full app launch for the 'start a workout' action specifically, since Tiles load near-instantly compared to a full Compose for Wear OS app cold start, which mattered because the entire value proposition of quick-start on a watch evaporates if there's a multi-second loading spinner before the button appears.
Complications, Apple Watch face widgets showing live data like current pace, added real ongoing value but came with their own budget constraint — ClockKit limits how often a complication can update, and we had to design the data refresh strategy around that budget rather than pushing updates as often as the underlying workout data actually changed, settling on a refresh interval that stayed within budget while still feeling responsive during a typical 30-45 minute run.

