See how clinics ship software 3× faster with Epica
Home / Blog / Engineering
Engineering

Native iOS App Architecture for Remote Patient Monitoring

A remote patient monitoring app isn't a health-tracking app with a clinical label. It has to survive a closed app, a dropped sensor, and a conflicting reading without ever quietly losing data a clinician will act on. Four architectural decisions — background sync, offline-first storage, conflict resolution, and FHIR integration — determine whether it holds up in a real clinic.

Abstract iOS device interface showing health data dashboards for remote patient monitoring

What Makes a Remote Patient Monitoring App Different From a Regular Health App?

A consumer health app is built around convenience: if a step count syncs late or a sleep session gets dropped, nothing downstream changes. A remote patient monitoring (RPM) app is built around a clinical decision loop — a care coordinator reviews a trend, flags a deviation, and sometimes calls a patient in early based on what the app reported. That changes the engineering bar entirely. A missed reading isn't a UX nitpick; it's a gap in the record a clinician assumes is complete. Working with a native iOS development team like YuSMP Group on this kind of build means treating data completeness and timestamp integrity as core requirements from the first sprint, not polish added before launch.

That shift shows up in four places: how reliably the app captures data in the background, how it behaves without a network connection, how it resolves two readings that disagree, and how cleanly it hands data to the systems a clinic already runs on. Get those four right and the rest — UI, onboarding, notifications — is comparatively easy. Get them wrong and no amount of polish fixes a chart with silent gaps in it.

The Core Architecture, From Sensor to Clinic System

Most native iOS RPM apps share the same layered shape, even when the specific sensors differ:

  1. Sensor or wearable. A Bluetooth Low Energy (BLE) blood pressure cuff, pulse oximeter, glucose meter, or an Apple Watch reading vitals directly.
  2. Gateway device (the iPhone). Pairs with the sensor, receives readings via BLE or HealthKit, and is the only point where raw data briefly exists before it's persisted.
  3. Local store. Writes the reading to an on-device database immediately, before any network call is attempted.
  4. Sync layer. Queues and uploads persisted readings to the backend, retrying on failure, deduplicating on success.
  5. Clinical system. The backend maps incoming readings into FHIR resources the EHR or care-coordination platform already consumes.

The layer that gets underbuilt most often is the third one — the local store. Teams that treat the phone as a pass-through pipe from sensor to cloud build an app that works perfectly in a demo and loses data the first time a patient loses signal on a bus.

Getting HealthKit Background Delivery Right

When vitals come through Apple Watch or a HealthKit-integrated sensor rather than raw BLE, background delivery is what keeps the app capturing data while it's not open. HKObserverQuery registers interest in a given sample type and wakes the app when new data lands; Apple's HealthKit background delivery documentation is the starting reference for how the observer and delivery-frequency APIs fit together. The entitlement com.apple.developer.healthkit.background-delivery has to be requested explicitly, and delivery is registered per sample type — heart rate and blood oxygen don't share one subscription, so an app monitoring both needs both set up and both tested independently.

The part that trips up first-time implementations: background delivery isn't a guarantee, it's a best-effort signal subject to iOS's system-level power management. Under aggressive battery optimization, delivery can be delayed from seconds to hours, and the app has no reliable way to force an earlier wake. The correct response isn't fighting the OS — it's designing the sync layer to assume delayed delivery is normal and to reconcile timestamps on arrival rather than on wall-clock receipt time, so a reading that arrives three hours late is still correctly placed on the trend line where it belongs.

Designing the Offline-First Local Store

An RPM app that assumes a live connection breaks exactly when it matters most — a patient in a rural area, in a basement exam room, or simply between Wi-Fi and cellular handoff. Every reading has to land in a local database (Core Data or SQLite both work; the choice matters less than the discipline around it) before any network call is attempted, full stop. The sync layer then becomes a background job that drains a queue, not a dependency the capture path waits on.

Idempotency on the write path matters as much as the local store itself: if a sync retry fires twice because the first attempt's acknowledgment was lost on a flaky connection, the backend needs to recognize the duplicate by a stable client-generated reading ID, not insert it twice and silently inflate a trend. We go deeper on the local-storage and sync-engine patterns that apply across clinic software generally in our offline-first architecture piece — the same discipline applies here, just with vitals instead of charting data.

Handling Sync Conflicts and Data Integrity

Two sources of truth can disagree: a device-reported BLE reading and a value a patient or nurse enters manually to correct it. Silently letting the newer write overwrite the older one — plain last-write-wins — is the wrong default for clinical data, because it erases evidence of what the sensor actually captured without a trace.

A safer pattern keeps both: version each reading, tag manual corrections with a distinct source flag, and preserve the original device value even when a corrected value becomes the one surfaced to a clinician by default. That way a care team reviewing an anomaly can see not just "what's current" but "what changed and who changed it" — which matters the first time a flagged vital turns out to be a data-entry question rather than a clinical one.

Securing Patient Data at Rest and in Transit

Three layers need to hold independently. Locally, encryption keys belong in the iOS Keychain, never in app defaults or a plist, and the local database itself should be encrypted at rest so a lost or jailbroken device doesn't expose a patient's reading history. In transit, TLS with certificate pinning closes the gap a compromised network or rogue proxy could otherwise exploit between phone and backend. And in logging — the layer teams forget — crash reports and diagnostic logs need to be scrubbed of PHI before they leave the device; a stack trace that includes a patient's heart-rate value or name is a compliance incident waiting to be discovered during an audit, not before.

None of this is exotic. It's table stakes for any HIPAA-aware build, and it's cheapest to get right in the first architecture pass rather than retrofitted after a security review flags it.

Battery Life and Background Throttling Tradeoffs

Continuous high-frequency polling drains a battery fast enough that patients stop wearing the sensor or keeping the app open — the single most common way an RPM program quietly fails, with no error message, no crash, just adherence dropping off a cliff. The fix is an adaptive polling interval rather than one fixed rate: critical vitals that a care protocol flags as high-priority can poll more frequently, while routine readings fall back to a longer interval that respects iOS's background execution budget.

This is a genuine tradeoff, not a solved problem — tighter polling means fresher data for the clinician and a shorter battery life for the patient. The right interval depends on the clinical protocol driving the monitoring program, not on an engineering default picked without clinical input.

Connecting the App to Clinic Systems via FHIR

Readings that only live in a proprietary app database are clinically invisible — a care team working inside its EHR won't go looking in a separate vendor portal for a vital sign. The fix is mapping captured readings into FHIR Observation resources, following the structure laid out in the HL7 FHIR specification, with the correct LOINC code, unit, and a reference back to the patient record the EHR already recognizes.

A proprietary API that works for one pilot clinic becomes a maintenance burden the moment a second clinic with a different EHR wants the same app — each new integration is bespoke work instead of a standard mapping. Building the FHIR layer once, early, is what lets the same native app connect to a different clinic's system without a rewrite. We cover the broader integration patterns in our FHIR integration guide for clinics.

Testing Strategy for Clinical-Grade Reliability

Standard mobile QA — tap through the flows, check the UI renders — doesn't catch the failure modes that matter here. Three tests are specific to RPM apps and easy to skip under deadline pressure:

  • Network-loss simulation at the sync boundary. Kill connectivity mid-upload, confirm the queue retains the reading and retries without duplicating it.
  • Load testing the offline queue. Simulate a patient who's been offline for days accumulating readings, then reconnects — confirm the burst upload doesn't drop, reorder, or rate-limit silently.
  • End-to-end verification from sensor to EHR. Take one known reading all the way from the BLE device through local storage, sync, FHIR mapping, and into the clinical system, and confirm the value, timestamp, and patient match at every hop.

The third test is the one most teams never run because it crosses the whole stack rather than a single component. It's also the one that catches the bug that actually matters: a reading that's correct in the app but arrives at the EHR with the wrong patient reference or a shifted timestamp.

Need this done right? Epica builds medical software end to end. Talk to us →

FAQ

Can a remote patient monitoring app use HealthKit data directly in a clinical workflow?

Not directly as a system of record. HealthKit data needs to be mapped into a FHIR-shaped clinical schema, with proper coding and a reference to the patient record, before it's treated as clinically actionable — the same gap that applies to Android's Health Connect applies here.

Why not just build a hybrid/cross-platform app instead of native iOS?

Background delivery, BLE device integration, and HealthKit access are areas where cross-platform frameworks add a translation layer between the app and APIs that are themselves platform-specific and change yearly. For an RPM app where background reliability is the core requirement, that extra layer is exactly the risk worth avoiding.

How does background delivery behave when the patient closes the app?

HealthKit background delivery can still wake the app to process new samples even when it's not open in the foreground, subject to iOS's power management. It's best-effort, not guaranteed-latency — the architecture needs to assume delayed delivery is normal, not an edge case.

What happens to patient data if the Apple Watch or sensor disconnects mid-reading?

A well-designed capture layer only persists a reading once it's complete and validated; a partial or interrupted reading should be discarded rather than stored as a potentially misleading data point. The sensor and app should retry the reading rather than submit a fragment.

Is Core Data or SQLite the better choice for offline-first clinical data?

Both are viable; the choice matters less than whether the write-before-sync discipline and idempotent sync keys are implemented consistently. Teams already invested in Core Data for other app state generally stay there rather than introduce a second persistence layer.

How do you handle a conflicting vital-sign reading from two different sources?

Preserve both the original device reading and any manual correction, tagged with source and version, rather than silently overwriting one with the other. A clinician reviewing an anomaly needs to see what the sensor actually reported, not just the most recent value.