Requirements
Coverage figures: Statcounter, August 2026.
The torch is required. The fingertip reading depends on steady, bright light against the skin. A device with a
rear camera but no torch is reported as unsupported. This is why most iPads and many tablets are unsupported,
and why simulators and emulators are always unsupported.
Native Swift and Kotlin, each adding under 1 MB to your app download.
Checking support at runtime
CalldeviceSupport() before showing the feature. It never opens the camera (iOS reads the capture formats,
Android the Camera2 characteristics), so it is cheap to call at any time. DeviceSupport has exactly five
fields.
reason is a short English string for your logs (localize your own copy):
On Android, a device whose camera characteristics cannot be read (or that advertises no fps ranges) counts as
supported; the runtime frame-rate gate then decides during the reading.
Creating a session on an unsupported device throws
unsupportedDevice(reason) (iOS VitalsSession.init, Android
VitalsSDK.createSession), and start() checks again, so the check is a convenience for your UI rather than a
gate you must implement.
Frame rate and power modes
- iOS starts at the highest tier
deviceSupport().fpsTierreports (60 on current iPhones) and drops to 30, then 20, when the camera cannot hold 70% of the tier for two seconds (Low Power Mode is the usual cause). Each drop is reported withreconfiguringCamera(lowPowerMode)and the camera is reconfigured in place. A device that cannot hold the last tier fails withunsupportedDevice. - Android runs a fixed 30 fps. Battery Saver and thermal throttling can lower it; while positioning, six
consecutive previews under 24 fps report
recoverableError(frameRateUnsustainable(fps))and the SDK re-arms once; a second recoverable error fails the session withunsupportedDevice. While measuring, a low rate is recorded in the diagnostics only.
Android camera capabilities
The SDK configures the camera for the reading (manual exposure where the device offers it, an exposure bias elsewhere) and clears that configuration again when the session stops, so your own camera features start from CameraX defaults afterwards. Devices differ in how many camera controls they expose; on devices with fewer controls, positioning can take a few seconds longer while exposure settles (capped at 15 s). Readings work either way, and there is nothing you need to handle. Thediagnostics on each result record the camera’s operating
point, so you can see what a given device did.
Lifecycle requirements
A reading needs the app in the foreground for the whole minute: backgrounding (iOS) or the lifecycle owner’sON_STOP (Android) during positioning or measuring fails the session with interrupted. Lock the reading
screen’s orientation (an Android rotation recreates the activity) and keep the screen on.
Excluding device models (deny-list override)
The SDK has no remote configuration and no built-in list of excluded models:deviceSupport() answers from the
hardware alone. If a model misbehaves in your user base, exclude it on your side, in front of the SDK, so you
can change the list without shipping a new build. This is how the NEUROFIT app does it: a list of model tokens
in its own remote config, matched case-insensitively by containment against the device model identifier, so an
entry can be an exact model or a family prefix.
Model identifiers: on iOS the machine identifier (iPhone14,2 style, from uname or
sysctlbyname("hw.machine"); the SDK reports the same string as CancelContext.deviceModel); on Android
Build.MODEL (for example SM-G991B; CancelContext.deviceModel prefixes the manufacturer, Samsung SM-G991B).
What the validation covered
The validation study ran on 21 iPhone and 9 Android participants’ own phones. Accuracy on iOS was 2.81 ms mean absolute HRV (RMSSD) error and on Android 4.58 ms; see Metrics. Devices outside the study are supported when they meet the requirements above; the SDK’s start gate and quality tier are what protect the numbers on any given phone, and thewithheld tier is what tells you when they could not.
Known limitations
- iPads and tablets without a torch are unsupported.
- Cases and lens protectors that separate the flash from the lens can weaken the signal. Suggest removing a thick
case if readings keep restarting with
weakSignal(iOS) orlowQuality. - Cold fingers give a faint pulse. Suggest warming the hands first.
- Simulators and emulators have no camera and are unsupported; test on a device.
- A device without an accelerometer still takes readings; the breathing rate then comes from the pulse signal
alone and
diagnostics.motion_samplesis 0.