Unified permissions API for React Native on iOS, Android and Windows platforms
—
iOS location accuracy control, Android alarm permissions, and other platform-specific functionality that extends beyond basic permission checking and requesting.
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>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 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();
}
}
}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;
}// 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-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();
}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
);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
}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