React-native-permissions: iOS microphone permission pop up not appearing on real device

Created on 25 Feb 2021  Â·  10Comments  Â·  Source: zoontek/react-native-permissions

Bug report

Summary

In my app user has to tap a microphone to send an audio message, before opening the voice record bottom sheet I need to check for microphone permission. The native iOS microphone permission pop up won't open after requesting permission. I had to change the RNPermissionHandlerMicrophone.m to the following:

#import "RNPermissionHandlerMicrophone.h"

@import AVFoundation;

@implementation RNPermissionHandlerMicrophone

+ (NSArray<NSString *> * _Nonnull)usageDescriptionKeys {
  return @[@"NSMicrophoneUsageDescription"];
}

+ (NSString * _Nonnull)handlerUniqueId {
  return @"ios.permission.MICROPHONE";
}

- (void)checkWithResolver:(void (^ _Nonnull)(RNPermissionStatus))resolve
                 rejecter:(void (__unused ^ _Nonnull)(NSError * _Nonnull))reject {
  switch ([[AVAudioSession sharedInstance] recordPermission]) {
    case AVAudioSessionRecordPermissionUndetermined:
      return resolve(RNPermissionStatusNotDetermined);
    case AVAudioSessionRecordPermissionDenied:
      return resolve(RNPermissionStatusDenied);
    case AVAudioSessionRecordPermissionGranted:
      return resolve(RNPermissionStatusAuthorized);
  }
}

- (void)requestWithResolver:(void (^ _Nonnull)(RNPermissionStatus))resolve
                   rejecter:(void (^ _Nonnull)(NSError * _Nonnull))reject {
  [AVCaptureDevice requestAccessForMediaType:AVMediaTypeAudio
                           completionHandler:^(__unused BOOL granted) {
    [self checkWithResolver:resolve rejecter:reject];
  }];
}

@end

After applying this change the native pop up kicks in after I request for permission and it works as expected.

I patch-package'd the module, patch-package crashes so we need to patch patch-package using patch-package
Here's a patch react-native-permissions+3.0.0.patch:

diff --git a/node_modules/react-native-permissions/ios/Microphone/RNPermissionHandlerMicrophone.m b/node_modules/react-native-permissions/ios/Microphone/RNPermissionHandlerMicrophone.m
index 5885228..0bac37d 100644
--- a/node_modules/react-native-permissions/ios/Microphone/RNPermissionHandlerMicrophone.m
+++ b/node_modules/react-native-permissions/ios/Microphone/RNPermissionHandlerMicrophone.m
@@ -14,14 +14,12 @@ + (NSString * _Nonnull)handlerUniqueId {

 - (void)checkWithResolver:(void (^ _Nonnull)(RNPermissionStatus))resolve
                  rejecter:(void (__unused ^ _Nonnull)(NSError * _Nonnull))reject {
-  switch ([AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio]) {
-    case AVAuthorizationStatusNotDetermined:
+  switch ([[AVAudioSession sharedInstance] recordPermission]) {
+    case AVAudioSessionRecordPermissionUndetermined:
       return resolve(RNPermissionStatusNotDetermined);
-    case AVAuthorizationStatusRestricted:
-      return resolve(RNPermissionStatusRestricted);
-    case AVAuthorizationStatusDenied:
+    case AVAudioSessionRecordPermissionDenied:
       return resolve(RNPermissionStatusDenied);
-    case AVAuthorizationStatusAuthorized:
+    case AVAudioSessionRecordPermissionGranted:
       return resolve(RNPermissionStatusAuthorized);
   }
 }

Environment info

react-native info output:

System:
    OS: macOS 11.2.1
    CPU: (8) x64 Intel(R) Core(TM) i5-8279U CPU @ 2.40GHz
    Memory: 959.40 MB / 8.00 GB
    Shell: 5.8 - /bin/zsh
  Binaries:
    Node: 12.18.3 - ~/.nvm/versions/node/v12.18.3/bin/node
    Yarn: 1.22.10 - ~/.nvm/versions/node/v12.18.3/bin/yarn
    npm: 7.5.6 - ~/.nvm/versions/node/v12.18.3/bin/npm
    Watchman: 4.9.0 - /usr/local/bin/watchman
  Managers:
    CocoaPods: 1.10.1 - /Users/stathis/.rbenv/shims/pod
  SDKs:
    iOS SDK:
      Platforms: iOS 14.4, DriverKit 20.2, macOS 11.1, tvOS 14.3, watchOS 7.2
    Android SDK:
      API Levels: 26, 28, 29
      Build Tools: 28.0.3, 29.0.2, 29.0.3, 30.0.2
      System Images: android-27 | Google APIs Intel x86 Atom
      Android NDK: Not Found
  IDEs:
    Android Studio: 4.1 AI-201.8743.12.41.7042882
    Xcode: 12.4/12D4e - /usr/bin/xcodebuild
  Languages:
    Java: 1.8.0_275 - /usr/bin/javac
    Python: 2.7.16 - /usr/bin/python
  npmPackages:
    @react-native-community/cli: Not Found
    react: 16.13.1 => 16.13.1 
    react-native: ^0.63.4 => 0.63.4 
    react-native-macos: Not Found
  npmGlobalPackages:
    *react-native*: Not Found

Library version: 3.0.0

Steps to reproduce

Just create a request for iOS microphone permission.

Describe what you expected to happen:

Open the native iOS pop up requesting microphone permission.

Reproducible sample code

bug

Most helpful comment

I made a release with the fix: https://github.com/zoontek/react-native-permissions/releases/tag/3.0.1 (tried it on the simulator and a device, it works perfectly well)

Thanks for your help!

All 10 comments

@efstathiosntonas Thank you! Can you please open a pull request instead?

@zoontek sure

It seems a bit weird that updating the checkWithResolver method fix the request. You are still calling [AVCaptureDevice requestAccessForMediaType:AVMediaTypeAudio completionHandler:] which is responsible for showing the popup.

this is the code:

#import "RNPermissionHandlerMicrophone.h"

@import AVFoundation;

@implementation RNPermissionHandlerMicrophone

+ (NSArray<NSString *> * _Nonnull)usageDescriptionKeys {
  return @[@"NSMicrophoneUsageDescription"];
}

+ (NSString * _Nonnull)handlerUniqueId {
  return @"ios.permission.MICROPHONE";
}

- (void)checkWithResolver:(void (^ _Nonnull)(RNPermissionStatus))resolve
                 rejecter:(void (__unused ^ _Nonnull)(NSError * _Nonnull))reject {
  switch ([[AVAudioSession sharedInstance] recordPermission]) {
    case AVAudioSessionRecordPermissionUndetermined:
      return resolve(RNPermissionStatusNotDetermined);
    case AVAudioSessionRecordPermissionDenied:
      return resolve(RNPermissionStatusDenied);
    case AVAudioSessionRecordPermissionGranted:
      return resolve(RNPermissionStatusAuthorized);
  }
}

- (void)requestWithResolver:(void (^ _Nonnull)(RNPermissionStatus))resolve
                   rejecter:(void (^ _Nonnull)(NSError * _Nonnull))reject {
  [AVCaptureDevice requestAccessForMediaType:AVMediaTypeAudio
                           completionHandler:^(__unused BOOL granted) {
    [self checkWithResolver:resolve rejecter:reject];
  }];
}

@end


i think the issue is in this line: [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio], this line won't call it immediately for some reason and it returns undetermined

This is only used to check permission, not triggering the popup. See iOS documentation: https://developer.apple.com/documentation/avfoundation/avcapturedevice/1624613-authorizationstatusformediatype

This method perform incorrectly on the simulator, but not on real devices (https://stackoverflow.com/questions/32988277/avauthorizationstatusnotdetermined-on-ios-simulator)

Did you enabled Microphone capability on Xcode? Which device are you using (and with which iOS version)?

I'm on Xcode 12.4, ios 14.4, iPhone XS and I have enabled microphone capability on Xcode (NSMicrophoneUsageDescription on info.plist).

On the so link you provided, the answer provided uses the same logic as my changes hmmm

edit: witch one would be better to use though? I mean for the package it self. Should we add one more request type that forces the pop up to open?

@efstathiosntonas Not according to Apple documentation:

Screenshot 2021-02-25 at 12 12 16

[AVAudioSession sharedInstance] also expose a requestRecordPermission method. It's probably better to change the check and the request to use this API if it's perform better:

https://developer.apple.com/documentation/avfaudio/avaudiosession/1616601-requestrecordpermission?language=objc

I made a release with the fix: https://github.com/zoontek/react-native-permissions/releases/tag/3.0.1 (tried it on the simulator and a device, it works perfectly well)

Thanks for your help!

thanks @zoontek, was going to PR it later but you got me first 😀

Was this page helpful?
0 / 5 - 0 ratings