Skip to main content

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

Call deviceSupport() 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().fpsTier reports (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 with reconfiguringCamera(lowPowerMode) and the camera is reconfigured in place. A device that cannot hold the last tier fails with unsupportedDevice.
  • 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 with unsupportedDevice. While measuring, a low rate is recorded in the diagnostics only.
Ask users to turn off Low Power Mode or Battery Saver before a reading if they hit these paths. The NEUROFIT app shows “Adjusting camera resolution - please turn off Low Power Mode.” at that moment.

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. The diagnostics 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’s ON_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).
When the check fails, do not create a session: hide the feature or show your own “not available on this device” copy. Keep the list short and dated, and remove entries once a fix ships.

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 the withheld 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) or lowQuality.
  • 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_samples is 0.