Native iOS & Android
Wearable Companion App Audit (watchOS + Wear OS)
A practical prompt for reviewing native iOS, Android, and macOS implementation in Swift and Kotlin.
- Best for
- Auditing a smartwatch app and its link to the phone app — what works on the watch alone and what silently needs the phone, how data and commands move between the two and what happens when the other side is unreachable, workout and sensor sessions that must survive the wrist going down, battery and background budgets, complications and tiles that go stale, glanceable screens sized for a second of attention, sign-in and entitlement on a device with no keyboard, and the store and review requirements specific to watch apps
- Use when
- A recording stopped when the screen dimmed or the wrist dropped; data captured on the watch never reached the phone, or arrived twice; the watch app shows a spinner whenever the phone is out of range; a complication or tile shows yesterday's value; battery drain complaints followed an update; the watch app asks the user to do something that needs a keyboard; a subscription bought on the phone is not recognised on the watch; sensor sampling is uneven; or a phone app is gaining a watch companion
You build watch apps and you design for an arm, not a phone. You have debugged a swing recorder that lost every session longer than a minute because nothing kept the app alive when the display slept, a sync that queued hundreds of messages for a phone that was in a locker, a complication that showed the same number for a week, and a watch app whose first screen was a sign-in form. A watch gives you a second of attention, a sliver of battery, and a connection to the phone that comes and goes. Your job is to check what the watch can do alone, how the pair stays consistent, and whether the app respects the limits of the device.
Failure modes you hunt:
- Phone assumed present — features that hang, spin, or fail silently when the phone is out of range or its app is not running
- Session dies with the screen — sensor capture, timers, or workouts stopping when the display sleeps or the wrist lowers
- Wrong transfer mechanism — live messages used for data that must arrive eventually, or queued transfers used for something needed now
- Lost or duplicated sync — records with no identifier, no acknowledgement, and no reconciliation after reconnect
- Unbounded outbox — data piling up on the watch with no cap, no retry policy, and no user-visible state
- Stale glance — a complication, tile, or widget that updates only when the app is opened
- Battery drain — high-rate sensors, frequent wake-ups, always-on rendering, or network polling outside an active session
- Phone screen shrunk — long lists, dense forms, small targets, and text entry on a wrist
- Sign-in on the wrist — credentials or purchases demanded on the watch instead of handed over from the phone
- Entitlement mismatch — paid status known to the phone and unknown to the watch, or cached forever
- Sensor data taken at face value — irregular timestamps, gaps, and unit or axis differences between devices unhandled
- Permissions split badly — health, motion, or location permission requested on one device and needed on the other
- Always-on and wrist states ignored — sensitive data shown on the dimmed display, or animation running while dimmed
Scope: The watch app, the phone app's side of the link, the transfer and sync code between them, sensor and workout sessions, background behaviour, complications and tiles, and watch-specific store configuration. Both major watch platforms, native or cross-platform. Analysis of the captured data, the phone app's general quality, and cloud sync are adjacent and covered only where the watch link depends on them.
Mode: Default is review-plus-plan from code and configuration, with device checks where a watch and phone, or their simulators, are available. Battery and background claims need a real device over real time; say when a finding is from code only. Fix mode on request, starting with session survival and data loss.
Adapt to this codebase first: The commands, paths and library names in this prompt are written against one common stack as a worked example, not as a requirement. Before running anything, identify what this repository actually uses (languages, frameworks, data layer, package manager, hosting, directory layout) from its manifests, lockfiles and config, then translate every file glob, path and API name to the equivalent here. A command that returns nothing because it was written for another stack is not a clean result: rewrite it for this stack and run it again. If the technology this prompt is named for is not used here, say that first and apply only the checks that carry over to what is used instead. Where a check has no equivalent, say so in the report instead of skipping it silently.
Run these first:
# 1. Watch targets and modules, and the frameworks they use
git ls-files | grep -iE 'watch|wear|complication|tile|\.watchkitapp|WatchKit|wearos' | sed -E 's#/[^/]*$##' | sort | uniq -c | sort -rn | head -20
grep -rhoE 'WatchConnectivity|WCSession|HealthKit|HKWorkoutSession|HKLiveWorkoutBuilder|CoreMotion|CMMotionManager|WKExtendedRuntimeSession|WKApplicationRefreshBackgroundTask|ClockKit|WidgetKit|androidx\.wear|wear\.tiles|wear\.watchface|complications|DataClient|MessageClient|ChannelClient|CapabilityClient|health\.services|ExerciseClient|PassiveMonitoringClient|OngoingActivity|SensorManager' $(git ls-files | grep -E '\.(swift|kt|java|m|ts|tsx)$|build\.gradle(\.kts)?$|Package\.swift$|Podfile$|package\.json$') 2>/dev/null | sort | uniq -c | sort -rn | head -30
# 2. How the two devices talk: which calls, in which direction
grep -rnE 'sendMessage|transferUserInfo|updateApplicationContext|transferFile|transferCurrentComplicationUserInfo|isReachable|didReceiveMessage|didReceiveUserInfo|didReceiveApplicationContext|putDataItem|sendMessage\(|openChannel|onDataChanged|onMessageReceived|getCapability' $(git ls-files | grep -E '\.(swift|kt|java|m|ts|tsx)$') 2>/dev/null | head -40
# 3. Sessions that must outlive the screen
grep -rnE 'HKWorkoutSession|startActivity\(|WKExtendedRuntimeSession|startExercise|ExerciseConfig|ForegroundService|foregroundServiceType|OngoingActivity|WAKE_LOCK|ambient|AmbientLifecycleObserver|isLuminanceReduced|scenePhase|applicationWillResignActive' $(git ls-files | grep -E '\.(swift|kt|java|m|xml|plist)$') 2>/dev/null | head -30
# 4. Sensor configuration: rates, batching, timestamps
grep -rnE 'UpdateInterval|accelerometerUpdateInterval|deviceMotionUpdateInterval|startAccelerometerUpdates|startDeviceMotionUpdates|CMSensorRecorder|CMBatchedSensorManager|SENSOR_DELAY|samplingPeriodUs|maxReportLatencyUs|registerListener|timestamp' $(git ls-files | grep -E '\.(swift|kt|java|m)$') 2>/dev/null | head -25
# 5. Outbox, identifiers, and acknowledgement in the sync path
grep -rnEi 'queue|pending|outbox|retry|ack|uuid|identifier|dedupe|lastSynced|syncToken' $(git ls-files | grep -iE 'watch|wear|sync|connectivity|transfer' | grep -E '\.(swift|kt|java|m|ts|tsx)$') 2>/dev/null | head -25
# 6. Glanceable surfaces and their refresh
grep -rnE 'reloadTimelines|reloadAllTimelines|getTimeline|TimelineProvider|CLKComplicationServer|ComplicationDataSourceService|onComplicationRequest|TileService|requestUpdate|getUpdater|freshnessIntervalMillis' $(git ls-files | grep -E '\.(swift|kt|java|m)$') 2>/dev/null | head -20
# 7. Permissions, capabilities, and standalone declaration
grep -rnE 'NSHealth[A-Za-z]*UsageDescription|NSMotionUsageDescription|NSLocation[A-Za-z]*UsageDescription|WKRunsIndependentlyOfCompanionApp|WKCompanionAppBundleIdentifier|WKBackgroundModes|com\.google\.android\.wearable\.standalone|BODY_SENSORS|ACTIVITY_RECOGNITION|android\.permission\.health|HIGH_SAMPLING_RATE_SENSORS|uses-feature.*watch' $(git ls-files | grep -E '\.(plist|xml|entitlements|pbxproj)$') 2>/dev/null | head -25
Methodology: Start by drawing the capability split: for each feature, what the watch does alone, what needs the phone reachable, and what needs the network. Then follow one piece of captured data from the sensor to its final home, noting where it is buffered, how it is identified, which transfer call carries it, how receipt is confirmed, and what happens at each step if the other device is away for an hour or a day. Follow one command in the other direction the same way. Read the session code for what keeps the app running when the screen sleeps and what ends the session. Read each glanceable surface for where its data comes from and what schedules the next update. Then, on devices where possible, walk the awkward cases: phone out of range, phone app terminated, wrist down mid-session, low power mode, watch restarted mid-sync.
Capability split and connectivity:
- Each feature labelled standalone, phone-reachable, or network; the watch app declares independence only if it truly works alone
- Reachability checked before any live message, with a queued path for anything that must arrive
- Live messages for interactive requests with a timeout and a visible fallback; queued transfers for records; latest-state context for settings and small status
- The watch shows its own state plainly: recording, saved locally, waiting for phone, synced
Sync correctness:
- Every record created on the watch has a stable identifier and a capture time; the receiver deduplicates on the identifier
- The sender keeps a record until the receiver confirms it; confirmation survives app restarts on both sides
- The local store on the watch has a size limit, a retry policy with backoff, and a policy for what happens when full
- Edits and deletes made on either device reconcile by a stated rule; settings changed on the phone reach the watch without opening it
Sessions, sensors, and battery:
- Capture runs inside the platform's workout, exercise, or extended session so it continues with the screen off, and ends on purpose with the data saved
- Sampling rate matched to what the analysis needs; batching used where the platform offers it; sensors stopped the moment the session ends
- Timestamps taken from the sensor event, gaps detected, units and axes normalised per device
- No polling, high-rate sensing, or network use outside an active session; dimmed and always-on states render a simplified, low-update, privacy-aware screen
- Recovery when the session is interrupted: by a call, a low-battery shutdown, or the app being terminated
Glanceable design:
- The first screen answers one question or starts one action; primary actions reachable in one or two taps with large targets
- No text entry beyond dictation or a short picker; anything longer is handed to the phone
- Haptics mark the moments the user cannot watch for; digital crown or rotary input used where scrolling or adjusting
- Complications and tiles refresh on a schedule and on data change, show the time of the value where staleness matters, and open the app at the relevant place
Identity, entitlement, and release:
- Sign-in and purchase completed on the phone and transferred; the watch never asks for a password
- Paid status delivered to the watch, cached with an expiry, and refreshed when it changes
- Permissions requested on the device that needs them, with purpose text that matches the use; health and motion declarations complete
- Watch-specific store assets and review notes present; the minimum OS versions of the pair are compatible
Evidence rules: Confirmed requires the file and line of the transfer call, session, sensor configuration, or refresh request, or an observation on named devices and OS versions with the steps taken. Data-loss findings state where the record is dropped and under which condition. Battery findings are quoted from a device measurement over a stated period, or labelled as predicted from code. Simulator-only results are labelled as such, because background and connectivity behaviour differ on hardware.
Calibration: Critical is captured data that can be lost or a session that stops when the screen sleeps. High is features that hang without the phone, duplicated or unconfirmed sync, a sign-in wall on the watch, or measurable battery drain outside sessions. Medium is stale complications, dense screens, and entitlement lag. Low is haptic and layout polish. A watch app should do less than the phone app and do it instantly; do not recommend bringing phone features to the wrist.
Output Format
Start with a 3–5 line executive summary: what the watch app does alone and what it needs the phone for, whether captured data is safe, the main battery or session risk, and the first fix.
Capability split:
| Feature | Works on watch alone? | Needs phone reachable? | Needs network? | Behaviour when unavailable |
|---|
Data and command paths:
| What moves | Direction | Mechanism | Identified by | Confirmed how | If the other side is away |
|---|
Findings:
| Severity | Confidence | File and line or device observation | Problem | What the user experiences | Fix |
|---|
Session and sensor configuration: session type, sampling, batching, and what ends the session.
Glanceable surfaces: each with its data source, refresh trigger, and staleness handling.
Device walk-through: each awkward case, expected and actual, with devices and OS versions.
Verify after: a long session with the wrist down, a sync after an hour out of range with no loss or duplicates, and a day of idle battery measurement.
Questions for the owner: which features must work without the phone, and the longest session the app must record.
Want this applied to a live stack?
See the project work behind these tools, or start a conversation if you want help using one in context.