RNPermissionHandlerFaceID.m 3.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. #import "RNPermissionHandlerFaceID.h"
  2. @import LocalAuthentication;
  3. @import UIKit;
  4. @interface RNPermissionHandlerFaceID()
  5. @property (nonatomic, strong) LAContext *laContext;
  6. @property (nonatomic, strong) void (^resolve)(RNPermissionStatus status);
  7. @property (nonatomic, strong) void (^reject)(NSError *error);
  8. @end
  9. @implementation RNPermissionHandlerFaceID
  10. + (NSArray<NSString *> * _Nonnull)usageDescriptionKeys {
  11. return @[@"NSFaceIDUsageDescription"];
  12. }
  13. + (NSString * _Nonnull)handlerUniqueId {
  14. return @"ios.permission.FACE_ID";
  15. }
  16. - (void)checkWithResolver:(void (^ _Nonnull)(RNPermissionStatus))resolve
  17. rejecter:(void (^ _Nonnull)(NSError * _Nonnull))reject {
  18. if (@available(iOS 11.0.1, *)) {
  19. LAContext *context = [LAContext new];
  20. NSError *error;
  21. [context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error];
  22. bool hasFaceID = context.biometryType == LABiometryTypeFaceID;
  23. if (!hasFaceID) {
  24. return resolve(RNPermissionStatusNotAvailable);
  25. }
  26. if (error != nil) {
  27. if (error.code == LAErrorBiometryNotEnrolled)
  28. return resolve(RNPermissionStatusNotAvailable);
  29. if (error.code != LAErrorBiometryNotAvailable)
  30. return reject(error);
  31. if (hasFaceID)
  32. return resolve(RNPermissionStatusDenied);
  33. return resolve(RNPermissionStatusNotAvailable);
  34. }
  35. if (![RNPermissions isFlaggedAsRequested:[[self class] handlerUniqueId]]) {
  36. return resolve(RNPermissionStatusNotDetermined);
  37. }
  38. resolve(RNPermissionStatusAuthorized);
  39. } else {
  40. resolve(RNPermissionStatusNotAvailable);
  41. }
  42. }
  43. - (void)requestWithResolver:(void (^ _Nonnull)(RNPermissionStatus))resolve
  44. rejecter:(void (^ _Nonnull)(NSError * _Nonnull))reject {
  45. if (@available(iOS 11.0.1, *)) {
  46. LAContext *context = [LAContext new];
  47. NSError *error;
  48. [context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error];
  49. bool hasFaceID = context.biometryType == LABiometryTypeFaceID;
  50. if (!hasFaceID) {
  51. return resolve(RNPermissionStatusNotAvailable);
  52. }
  53. if (error != nil) {
  54. if (error.code == LAErrorBiometryNotEnrolled)
  55. return resolve(RNPermissionStatusNotAvailable);
  56. if (error.code != LAErrorBiometryNotAvailable)
  57. return reject(error);
  58. if (hasFaceID)
  59. return resolve(RNPermissionStatusDenied);
  60. return resolve(RNPermissionStatusNotAvailable);
  61. }
  62. _resolve = resolve;
  63. _reject = reject;
  64. _laContext = context;
  65. [[NSNotificationCenter defaultCenter] addObserver:self
  66. selector:@selector(UIApplicationDidBecomeActiveNotification:)
  67. name:UIApplicationDidBecomeActiveNotification
  68. object:nil];
  69. [context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics
  70. localizedReason:[[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSFaceIDUsageDescription"]
  71. reply:^(__unused BOOL success, __unused NSError * _Nullable error) {}];
  72. // Hack to invalidate FaceID verification immediately after being requested
  73. [self performSelector:@selector(invalidateContext) withObject:self afterDelay:0.05];
  74. } else {
  75. resolve(RNPermissionStatusNotAvailable);
  76. }
  77. }
  78. - (void)invalidateContext {
  79. [_laContext invalidate];
  80. }
  81. - (void)UIApplicationDidBecomeActiveNotification:(__unused NSNotification *)notification {
  82. [[NSNotificationCenter defaultCenter] removeObserver:self
  83. name:UIApplicationDidBecomeActiveNotification
  84. object:nil];
  85. [RNPermissions flagAsRequested:[[self class] handlerUniqueId]];
  86. [self checkWithResolver:_resolve rejecter:_reject];
  87. }
  88. @end