Skip to content

IOS - Crash on RNPermissions lockHandler #885

Open
@omerts

Description

@omerts

Before submitting a new issue

  • I tested using the latest version of the library, as the bug might be already fixed.
  • I tested using a supported version of react native.
  • I checked for possible duplicate issues, with possible answers.

Bug summary

Hello,

In Crashlytics, we are seeing the following crash when app is in the background:

          Crashed: com.apple.main-thread
0  libswiftCore.dylib             0x215928 __StringStorage.isEqualToString(to:) + 60
1  libswiftCore.dylib             0x215c64 @objc __StringStorage.isEqual(to:) + 92
2  CoreFoundation                 0x2114 -[__NSDictionaryM setObject:forKey:] + 348
3  LionWheel                      0x777c58 -[RNPermissions lockHandler:] + 300 (RNPermissions.mm:300)
4  LionWheel                      0x7782e4 -[RNPermissions request:resolve:reject:] + 341 (RNPermissions.mm:341)
5  CoreFoundation                 0x20814 __invoking___ + 148
6  CoreFoundation                 0x1f860 -[NSInvocation invoke] + 428
7  CoreFoundation                 0x961dc -[NSInvocation invokeWithTarget:] + 64
8  LionWheel                      0x828290 -[RCTModuleMethod invokeWithBridge:module:arguments:] + 587 (RCTModuleMethod.mm:587)
9  LionWheel                      0x82a394 facebook::react::invokeInner(RCTBridge*, RCTModuleData*, unsigned int, folly::dynamic const&, int, (anonymous namespace)::SchedulingContext) + 196 (RCTNativeModule.mm:196)
10 LionWheel                      0x829fe4 invocation function for block in facebook::react::RCTNativeModule::invoke(unsigned int, folly::dynamic&&, int) + 247 (optional:247)
11 libdispatch.dylib              0x213c _dispatch_call_block_and_release + 32
12 libdispatch.dylib              0x3dd4 _dispatch_client_callout + 20
13 libdispatch.dylib              0x125a4 _dispatch_main_queue_drain + 988
14 libdispatch.dylib              0x121b8 _dispatch_main_queue_callback_4CF + 44
15 CoreFoundation                 0x56710 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 16
16 CoreFoundation                 0x53914 __CFRunLoopRun + 1996
17 CoreFoundation                 0x52cd8 CFRunLoopRunSpecific + 608
18 GraphicsServices               0x11a8 GSEventRunModal + 164
19 UIKitCore                      0x40a90c -[UIApplication _run] + 888
20 UIKitCore                      0x4be9d0 UIApplicationMain + 340
21 LionWheel                      0x739c main + 8 (main.m:8)
22 ???                            0x1b01bde4c (Missing)

image

Podfile config:

def node_require(script)
  # Resolve script with node to allow for hoisting
  require Pod::Executable.execute_command('node', ['-p',
    "require.resolve(
      '#{script}',
      {paths: [process.argv[1]]},
    )", __dir__]).strip
end

node_require('react-native/scripts/react_native_pods.rb')
node_require('react-native-permissions/scripts/setup.rb')
  
platform :ios, '13.4'
prepare_react_native_project!

setup_permissions([
  # 'AppTrackingTransparency',
  #'Bluetooth',
  # 'Calendars',
  # 'CalendarsWriteOnly',
  'Camera',
  # 'Contacts',
  # 'FaceID',
  'LocationAccuracy',
  'LocationAlways',
  'LocationWhenInUse',
  #'MediaLibrary',
  'Microphone',
  # 'Motion',
  #'Notifications',
  'PhotoLibrary',
  # 'PhotoLibraryAddOnly',
  # 'Reminders',
  # 'Siri',
  # 'SpeechRecognition',
  # 'StoreKit',
])

Library version

4.1.5

Environment info

React Native:  0.73.6
(for some reason react-native info just stalls on my pc)

Steps to reproduce

Taken from Crashlytics, so not clear reproduction steps.

Reproducible sample code

Config:

def node_require(script)
  # Resolve script with node to allow for hoisting
  require Pod::Executable.execute_command('node', ['-p',
    "require.resolve(
      '#{script}',
      {paths: [process.argv[1]]},
    )", __dir__]).strip
end

node_require('react-native/scripts/react_native_pods.rb')
node_require('react-native-permissions/scripts/setup.rb')
  
platform :ios, '13.4'
prepare_react_native_project!

setup_permissions([
  # 'AppTrackingTransparency',
  #'Bluetooth',
  # 'Calendars',
  # 'CalendarsWriteOnly',
  'Camera',
  # 'Contacts',
  # 'FaceID',
  'LocationAccuracy',
  'LocationAlways',
  'LocationWhenInUse',
  #'MediaLibrary',
  'Microphone',
  # 'Motion',
  #'Notifications',
  'PhotoLibrary',
  # 'PhotoLibraryAddOnly',
  # 'Reminders',
  # 'Siri',
  # 'SpeechRecognition',
  # 'StoreKit',
])

Ask for permissions using this code:

import {request, requestMultiple, PERMISSIONS} from 'react-native-permissions'
import {Platform} from 'react-native'

function isPermissionGranted(permission) {
  if (permission && typeof permission === 'object') {
    return Object.values(permission).every(
      (val) => val === 'granted' || val === 'unavailable',
    )
  }

  return permission === 'granted' || permission === 'unavailable'
}

function getCameraPermissions() {
  return Platform.OS === 'ios'
    ? PERMISSIONS.IOS.CAMERA
    : PERMISSIONS.ANDROID.CAMERA
}

function getLocationPermisison(useBackgroundLocationApproved) {
  if (Platform.OS === 'ios') {
    return [PERMISSIONS.IOS.LOCATION_ALWAYS]
  } else {
    const permissions = [
      PERMISSIONS.ANDROID.ACCESS_FINE_LOCATION,
      PERMISSIONS.ANDROID.ACCESS_COARSE_LOCATION,
    ]

    // For android we show a warning about using background location which the user needs to approve according to
    // the play store guidelines
    if (useBackgroundLocationApproved) {
      permissions.push(PERMISSIONS.ANDROID.ACCESS_BACKGROUND_LOCATION)
    }

    return permissions
  }
}

function getMicrophonePermission() {
  return Platform.OS === 'ios'
    ? PERMISSIONS.IOS.MICROPHONE
    : PERMISSIONS.ANDROID.RECORD_AUDIO
}

export const checkPermissions = async (
  useBackgroundLocationApproved = true,
  recordCalls = false,
) => {
  // Android 11 and up doesn't allow background location
  if (parseInt(Platform.constants.Release) >= 11) {
    useBackgroundLocationApproved = false
  }

  const location = await requestMultiple(
    getLocationPermisison(useBackgroundLocationApproved),
  )

  const camera = await request(getCameraPermissions())

  let voice = null

  if (recordCalls) {
    voice = await request(getMicrophonePermission())
  }

  return {
    location: isPermissionGranted(location),
    camera: isPermissionGranted(camera),
    voice: isPermissionGranted(voice),
  }
}

export default checkPermissions

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions