CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-react-native-permissions

Unified permissions API for React Native on iOS, Android and Windows platforms

Pending
Overview
Eval results
Files

platform-specific-features.mddocs/

Platform-Specific Features

iOS location accuracy control, Android alarm permissions, and other platform-specific functionality that extends beyond basic permission checking and requesting.

Capabilities

iOS Location Accuracy Control

iOS 14+ provides users with the option to grant apps reduced location accuracy. These methods help manage location accuracy permissions.

/**
 * Check the current location accuracy authorization level (iOS only)
 * @returns Promise resolving to current location accuracy level
 */
function checkLocationAccuracy(): Promise<LocationAccuracy>;

/**
 * Request location accuracy upgrade from reduced to full (iOS only)
 * @param options - Configuration with purpose key from Info.plist
 * @returns Promise resolving to the granted accuracy level
 */
function requestLocationAccuracy(options: LocationAccuracyOptions): Promise<LocationAccuracy>;

type LocationAccuracy = 'full' | 'reduced';

interface LocationAccuracyOptions {
  purposeKey: string; // Must match a key in Info.plist NSLocationTemporaryUsageDescriptionDictionary
}

Usage Examples:

import { 
  checkLocationAccuracy, 
  requestLocationAccuracy,
  check,
  request,
  PERMISSIONS, 
  RESULTS 
} from "react-native-permissions";

async function handleLocationAccuracy() {
  // First ensure location permission is granted
  let locationStatus = await check(PERMISSIONS.IOS.LOCATION_WHEN_IN_USE);
  
  if (locationStatus !== RESULTS.GRANTED) {
    locationStatus = await request(PERMISSIONS.IOS.LOCATION_WHEN_IN_USE);
  }
  
  if (locationStatus === RESULTS.GRANTED) {
    // Check current accuracy level
    const accuracy = await checkLocationAccuracy();
    console.log("Current location accuracy:", accuracy);
    
    if (accuracy === 'reduced') {
      console.log("Location accuracy is reduced. Requesting full accuracy...");
      
      // Request full accuracy using Info.plist purpose key
      const newAccuracy = await requestLocationAccuracy({
        purposeKey: 'FindNearbyRestaurants' // Must be defined in Info.plist
      });
      
      if (newAccuracy === 'full') {
        console.log("Full location accuracy granted");
        // Now can access precise location
      } else {
        console.log("User chose to keep reduced accuracy");
        // Continue with approximate location
      }
    } else {
      console.log("Already have full location accuracy");
    }
  }
}

// Example usage in a restaurant finder app
async function findNearbyRestaurants() {
  const accuracy = await checkLocationAccuracy();
  
  if (accuracy === 'reduced') {
    // Explain why full accuracy is needed
    const upgraded = await requestLocationAccuracy({
      purposeKey: 'FindNearbyRestaurants'
    });
    
    if (upgraded === 'full') {
      // Use precise location for accurate results
      return searchRestaurantsWithPreciseLocation();
    } else {
      // Fall back to approximate location search
      return searchRestaurantsWithApproximateLocation();
    }
  } else {
    return searchRestaurantsWithPreciseLocation();
  }
}

Info.plist Configuration:

For requestLocationAccuracy to work, you must define purpose keys in your iOS app's Info.plist:

<key>NSLocationTemporaryUsageDescriptionDictionary</key>
<dict>
  <key>FindNearbyRestaurants</key>
  <string>Find the most relevant restaurants and delivery options near your exact location</string>
  <key>NavigationAccuracy</key>
  <string>Provide turn-by-turn navigation with precise location tracking</string>
</dict>

iOS Photo Picker Access

Provides access to the iOS photo picker without requiring full photo library permission.

/**
 * Open the iOS photo picker without requiring photo library permission
 * @returns Promise that resolves when picker is dismissed
 */
function openPhotoPicker(): Promise<void>;

Usage Examples:

import { openPhotoPicker, check, PERMISSIONS, RESULTS } from "react-native-permissions";

async function selectPhotos() {
  // iOS 14+ allows photo picker access without photo library permission
  try {
    await openPhotoPicker();
    console.log("Photo picker was opened successfully");
    // User can select photos through the picker
    // Selected photos are available to your app without library permission
  } catch (error) {
    console.error("Failed to open photo picker:", error);
    
    // Fallback: request full photo library permission
    const photoLibraryStatus = await check(PERMISSIONS.IOS.PHOTO_LIBRARY);
    if (photoLibraryStatus !== RESULTS.GRANTED) {
      console.log("Photo picker failed, requesting photo library permission...");
      // Handle full permission request
    }
  }
}

// Example in an image sharing app
async function addPhotosToPost() {
  if (Platform.OS === 'ios') {
    // Try photo picker first (no permission required)
    try {
      await openPhotoPicker();
      return; // Success - user can select photos
    } catch (error) {
      console.log("Photo picker not available, requesting full library access");
    }
  }
  
  // Android or iOS fallback - request full photo library permission
  const status = await request(PERMISSIONS.IOS.PHOTO_LIBRARY);
  if (status === RESULTS.GRANTED) {
    // Access full photo library
    openFullPhotoLibrary();
  }
}

Android Alarm Permissions

Android 12+ introduces restrictions on exact alarm scheduling. These methods help check alarm capabilities.

/**
 * Check if the app can schedule exact alarms (Android 12+)
 * @returns Promise resolving to boolean indicating exact alarm capability
 */
function canScheduleExactAlarms(): Promise<boolean>;

/**
 * Check if the app can use full screen intents (Android)
 * @returns Promise resolving to boolean indicating full screen intent capability  
 */
function canUseFullScreenIntent(): Promise<boolean>;

Usage Examples:

import { canScheduleExactAlarms, canUseFullScreenIntent, openSettings } from "react-native-permissions";

async function setupAlarmFeatures() {
  if (Platform.OS === 'android') {
    // Check exact alarm capability
    const canScheduleExact = await canScheduleExactAlarms();
    console.log("Can schedule exact alarms:", canScheduleExact);
    
    if (!canScheduleExact) {
      console.log("Exact alarms not available - directing user to settings");
      // Direct user to alarm settings
      await openSettings('alarms');
    }
    
    // Check full screen intent capability
    const canUseFullScreen = await canUseFullScreenIntent();
    console.log("Can use full screen intents:", canUseFullScreen);
    
    if (!canUseFullScreen) {
      console.log("Full screen intents not available");
      await openSettings('fullscreen');
    }
  }
}

// Example in an alarm clock app
async function scheduleAlarm(time: Date) {
  if (Platform.OS === 'android') {
    const canScheduleExact = await canScheduleExactAlarms();
    
    if (canScheduleExact) {
      // Schedule exact alarm
      await scheduleExactAlarm(time);
      console.log("Exact alarm scheduled");
    } else {
      // Show rationale and direct to settings
      Alert.alert(
        "Exact Alarms Required",
        "To ensure your alarms ring on time, please enable 'Alarms & reminders' in settings.",
        [
          { text: "Open Settings", onPress: () => openSettings('alarms') },
          { text: "Cancel", style: "cancel" }
        ]
      );
    }
  }
}

// Example for urgent notifications
async function sendUrgentNotification() {
  if (Platform.OS === 'android') {
    const canUseFullScreen = await canUseFullScreenIntent();
    
    if (canUseFullScreen) {
      // Show full screen notification for urgent alerts
      await showFullScreenNotification();
    } else {
      // Fall back to regular notification
      await showRegularNotification();
    }
  }
}

Settings Access

Direct users to specific system settings for permission management.

/**
 * Open device settings for permission management
 * @param type - Specific settings type to open (optional)
 * @returns Promise that resolves when settings are opened
 */
function openSettings(type?: 'application' | 'alarms' | 'fullscreen' | 'notifications'): Promise<void>;

Usage Examples:

import { openSettings, check, PERMISSIONS, RESULTS } from "react-native-permissions";

// Open general app settings
async function openAppSettings() {
  await openSettings('application');
  console.log("Opened app settings");
}

// Open specific settings based on permission state
async function handleBlockedPermission(permission: Permission) {
  const status = await check(permission);
  
  if (status === RESULTS.BLOCKED) {
    if (permission === PERMISSIONS.ANDROID.POST_NOTIFICATIONS) {
      await openSettings('notifications');
    } else {
      await openSettings('application');
    }
  }
}

// Handle blocked notification permissions (iOS uses checkNotifications/requestNotifications)
async function handleBlockedNotifications() {
  const notificationResult = await checkNotifications();
  
  if (notificationResult.status === RESULTS.BLOCKED) {
    await openSettings('notifications');
  }
}

// Platform-specific settings access
async function openPlatformSpecificSettings() {
  if (Platform.OS === 'android') {
    // Android-specific settings
    await openSettings('alarms');      // Alarm & reminders settings
    await openSettings('fullscreen');  // Full screen intent settings
  }
  
  // Common settings
  await openSettings('notifications'); // Notification settings
  await openSettings('application');  // General app settings
}

// Example usage in permission flow
async function requestPermissionWithFallback(permission: Permission) {
  let status = await check(permission);
  
  if (status === RESULTS.DENIED) {
    status = await request(permission);
  }
  
  if (status === RESULTS.BLOCKED) {
    Alert.alert(
      "Permission Required",
      "This permission has been blocked. Please enable it in Settings to continue.",
      [
        { 
          text: "Open Settings", 
          onPress: async () => {
            if (permission.includes('NOTIFICATION')) {
              await openSettings('notifications');
            } else {
              await openSettings('application');
            }
          }
        },
        { text: "Cancel", style: "cancel" }
      ]
    );
  }
  
  return status === RESULTS.GRANTED;
}

Platform-Specific Implementation Details

iOS Implementation Notes

// iOS-specific features are only available on iOS
import { Platform } from 'react-native';

if (Platform.OS === 'ios') {
  // Safe to use iOS-specific methods
  const accuracy = await checkLocationAccuracy();
  await openPhotoPicker();
  await requestLocationAccuracy({ purposeKey: 'MyPurpose' });
}

// iOS location accuracy requires iOS 14+
if (Platform.OS === 'ios' && parseInt(Platform.Version, 10) >= 14) {
  const accuracy = await checkLocationAccuracy();
}

Android Implementation Notes

// Android-specific features require specific Android versions
import { Platform } from 'react-native';

if (Platform.OS === 'android') {
  // Exact alarms introduced in Android 12 (API level 31)
  if (Platform.Version >= 31) {
    const canSchedule = await canScheduleExactAlarms();
  }
  
  // Full screen intents
  const canUseFullScreen = await canUseFullScreenIntent();
}

Error Handling for Platform-Specific Features

import { Platform } from 'react-native';

async function safePlatformSpecificCall<T>(
  androidFn?: () => Promise<T>,
  iosFn?: () => Promise<T>
): Promise<T | null> {
  try {
    if (Platform.OS === 'android' && androidFn) {
      return await androidFn();
    } else if (Platform.OS === 'ios' && iosFn) {
      return await iosFn();
    }
    return null;
  } catch (error) {
    console.error("Platform-specific method failed:", error);
    return null;
  }
}

// Usage
const accuracy = await safePlatformSpecificCall(
  undefined, // No Android equivalent
  () => checkLocationAccuracy()
);

const canSchedule = await safePlatformSpecificCall(
  () => canScheduleExactAlarms(),
  undefined // No iOS equivalent
);

Common Integration Patterns

Feature Detection Pattern

import { Platform } from 'react-native';

interface PlatformCapabilities {
  hasLocationAccuracy: boolean;
  hasPhotoPicker: boolean;
  hasExactAlarms: boolean;
  hasFullScreenIntents: boolean;
}

function detectPlatformCapabilities(): PlatformCapabilities {
  return {
    hasLocationAccuracy: Platform.OS === 'ios' && parseInt(Platform.Version, 10) >= 14,
    hasPhotoPicker: Platform.OS === 'ios' && parseInt(Platform.Version, 10) >= 14,
    hasExactAlarms: Platform.OS === 'android' && Platform.Version >= 31,
    hasFullScreenIntents: Platform.OS === 'android'
  };
}

// Adapt UI based on capabilities
const capabilities = detectPlatformCapabilities();
if (capabilities.hasLocationAccuracy) {
  // Show location accuracy toggle in settings
}

Progressive Enhancement Pattern

async function enhancedLocationRequest() {
  // Basic location permission
  const locationStatus = await request(PERMISSIONS.IOS.LOCATION_WHEN_IN_USE);
  
  if (locationStatus === RESULTS.GRANTED && Platform.OS === 'ios') {
    // Enhanced: Request full accuracy if available
    try {
      const accuracy = await checkLocationAccuracy();
      if (accuracy === 'reduced') {
        await requestLocationAccuracy({ purposeKey: 'PreciseLocation' });
      }
    } catch (error) {
      console.log("Location accuracy enhancement not available");
    }
  }
  
  return locationStatus;
}

Install with Tessl CLI

npx tessl i tessl/npm-react-native-permissions

docs

index.md

notification-permissions.md

permission-checking.md

permission-constants.md

permission-requesting.md

platform-specific-features.md

tile.json