Back to Home

react-native-device-proximity

by Isaías Chávez

A cross-platform React Native module for accessing the device proximity sensor using the New Architecture (JSI/TurboModules).

Features

  • Cross-platform: Works on both iOS and Android
  • New Architecture: Built with TurboModules for optimal performance
  • TypeScript: Full TypeScript support with type definitions
  • Event-driven: Subscribe to proximity changes in real-time
  • Sensor validation: Check if the proximity sensor is available and functional

Installation

npm install react-native-device-proximity

or

yarn add react-native-device-proximity

For iOS, install CocoaPods dependencies:

cd ios && pod install

Usage

Basic Example

import {
  isProximityAvailable,
  addProximityListener,
  removeProximityListener,
  validateSensor
} from 'react-native-device-proximity';

// Check if proximity sensor is available
const available = await isProximityAvailable();
console.log('Proximity sensor available:', available);

// Subscribe to proximity changes
const subscription = addProximityListener((data) => {
  console.log('Near:', data.near);
  console.log('Distance (Android only):', data.distance);
});

// Validate sensor functionality
const validation = await validateSensor(2000); // 2 second timeout
console.log('Sensor works:', validation.ok);
console.log('Reason:', validation.reason);
console.log('Samples received:', validation.samples);

// Unsubscribe when done
subscription.remove();
// or
removeProximityListener();

API Reference

isProximityAvailable(): Promise<boolean>

Checks if the proximity sensor is available on the device.

Returns: Promise<boolean> - true if sensor is available, false otherwise.

Example:

const available = await isProximityAvailable();

addProximityListener(callback)

Subscribes to proximity sensor changes.

Parameters:

  • callback: Function called when proximity changes
    • data.near (boolean): true if object is near the sensor
    • data.distance (number | null): Distance in cm (Android only, null on iOS)

Returns: EmitterSubscription - Subscription object with .remove() method

Example:

const subscription = addProximityListener((data) => {
  if (data.near) {
    console.log('Object detected nearby!');
  }
});

// Later...
subscription.remove();

removeProximityListener(): void

Removes all proximity listeners. Alternative to calling .remove() on the subscription.

Example:

removeProximityListener();

validateSensor(timeoutMs): Promise<ValidationResult>

Validates that the proximity sensor is functional.

Parameters:

  • timeoutMs (number): Timeout in milliseconds (Android only, ignored on iOS)

Returns: Promise<ValidationResult>

  • ok (boolean): true if sensor is functional
  • reason (string): Reason code (see Platform Differences below)
  • samples (number): Number of sensor events received

Example:

const result = await validateSensor(2000);

if (result.ok) {
  console.log('Sensor is working!');
} else {
  console.log('Sensor issue:', result.reason);
}

Platform Differences

Android

Sensor Characteristics

  • Type: Continuous sensor that reports distance values
  • Data: Returns actual distance in centimeters (typically 0-5cm range)
  • Events: Fires continuously at high frequency when registered
  • Validation: Counts sensor events during timeout period

validateSensor Behavior

  • ✅ Uses the timeoutMs parameter to wait for sensor events
  • ✅ Registers sensor with SENSOR_DELAY_FASTEST for maximum sampling
  • ✅ Counts how many events are received during the timeout
  • ✅ Returns validation result based on event count:
    • NO_EVENTS_IN_TIMEOUT: Sensor didn't respond
    • TOO_FEW_EVENTS__DOUBTFUL: Only 1 event received (suspicious)
    • EVENTS_RECEIVED: Multiple events received (sensor working)

Example result:

{
  ok: true,
  reason: "EVENTS_RECEIVED",
  samples: 47  // Received 47 events in 2 seconds
}

Proximity Detection

// Android proximity logic
const near = distance < 5.0; // Object within 5cm threshold

iOS

Sensor Characteristics

  • Type: Binary sensor (near/far states only)
  • Data: Only boolean state, no distance values
  • Events: Only fires when state changes (covered ↔ uncovered)
  • Validation: Checks if sensor can be enabled

Key Difference: iOS only notifies on state changes, not continuously like Android.

  • Only events on state change: UIDeviceProximityStateDidChangeNotification only fires when state changes from true → false or false → true
  • Few samples: In 2000ms you might receive 0, 1, or 2 events maximum (depending on user interaction)
  • Validation not sample-based: Can't validate by counting samples - sensor only notifies on state changes, not continuously
  • Binary only: Only states true/false, no distance values

validateSensor Behavior

  • Ignores timeoutMs parameter (validation is instant)
  • ✅ Simply attempts to enable proximityMonitoringEnabled
  • ✅ Checks if the property successfully changed
  • ✅ Immediately disables sensor after check
  • ✅ Returns result instantly:
    • SENSOR_AVAILABLE: Sensor can be enabled
    • SENSOR_NOT_AVAILABLE: Sensor cannot be enabled

Example result:

{
  ok: true,
  reason: "SENSOR_AVAILABLE",
  samples: 1  // Always 1 if available on iOS
}

Proximity Detection

// iOS proximity logic
const near = device.proximityState; // true/false only
const distance = null; // iOS doesn't provide distance

Comparison Table

FeatureAndroidiOS
Sensor TypeContinuousBinary (event on state change)
Distance Value✅ Yes (0-5cm typically)❌ No (null)
Event FrequencyHigh (hundreds/sec)Low (only on state change)
validateSensor DurationUses timeoutMsInstant (ignores timeoutMs)
Validation MethodCount events in timeoutCheck if sensor enables
Typical samples Value20-100+Always 1 (if available)
Thread SafetyHandler-basedMain thread dispatched

Important Notes

Android

  • The proximity sensor must be available for the validation to succeed
  • Validation requires the sensor to emit events, so results may vary if the sensor is already covered
  • Battery usage: Sensor uses power, so only enable when needed
  • Thread-safe implementation using AtomicInteger for listener counting

iOS

  • The sensor only notifies on state changes, not continuously
  • distance will always be null in the callback data
  • Validation is instant and doesn't require user interaction
  • The sensor may not work if covered when first enabled
  • All sensor operations are dispatched to the main thread for safety

Common Issues

"Sensor not responding" on Android

  • Make sure the device has a proximity sensor (some tablets don't)
  • Check if sensor is already covered when enabling
  • Try a longer timeout (3000-5000ms)

"No events received" on iOS

  • This is normal! iOS only fires events on state changes
  • Move your hand near/away from the sensor to trigger events
  • Don't rely on event count for validation on iOS

Events stop firing

  • Check if the listener was properly removed and re-added
  • Ensure the app is in the foreground
  • On Android, check if the sensor was unregistered

Example: Proximity-based Screen Lock

import { addProximityListener } from 'react-native-device-proximity';
import { useState, useEffect } from 'react';

function ProximityScreenLock() {
  const [isNear, setIsNear] = useState(false);

  useEffect(() => {
    const subscription = addProximityListener((data) => {
      setIsNear(data.near);

      if (data.near) {
        // Turn off screen or pause video
        console.log('Object nearby - locking screen');
      } else {
        // Turn on screen or resume video
        console.log('Object moved away - unlocking screen');
      }
    });

    return () => subscription.remove();
  }, []);

  return (
    <View>
      <Text>Proximity: {isNear ? 'NEAR' : 'FAR'}</Text>
    </View>
  );
}

Contributing

See the contributing guide to learn how to contribute to the repository and the development workflow.

License

MIT


Made with create-react-native-library