Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IOS requestAuthorization doesn't return success or error until you go into the settings and switch the permission value #335

Open
crasyboy42 opened this issue Oct 2, 2024 · 2 comments
Labels
bug Something isn't working

Comments

@crasyboy42
Copy link

crasyboy42 commented Oct 2, 2024

Environment

My machine (windows, build on a mac mini):

System:
  OS: Windows 10 10.0.19045
  CPU: (4) x64 Intel(R) Core(TM) i5-7600 CPU @ 3.50GHz
  Memory: 4.25 GB / 15.94 GB
Binaries:
  Node:
    version: 18.14.2
    path: C:\Program Files\nodejs_v18\node.EXE
  Yarn:
    version: 1.22.10
    path: ~\AppData\Roaming\npm\yarn.CMD
  npm:
    version: 9.5.0
    path: C:\Program Files\nodejs_v18\npm.CMD
  Watchman: Not Found
SDKs:
  Android SDK: Not Found
  Windows SDK: Not Found
IDEs:
  Android Studio: Not Found
  Visual Studio:
    - 17.10.35122.118 (Visual Studio Community 2022)
    - 16.11.33529.622 (Visual Studio Community 2019)
Languages:
  Java:
    version: 17.0.12
    path: /c/Program Files/Microsoft/jdk-17.0.12.7-hotspot/bin/javac
  Ruby: Not Found
npmPackages:
  "@react-native-community/cli": Not Found
  react:
    installed: 18.3.1
    wanted: 18.3.1
  react-native:
    installed: 0.75.3
    wanted: 0.75.3
  react-native-windows: Not Found
npmGlobalPackages:
  "*react-native*": Not Found
Android:
  hermesEnabled: true
  newArchEnabled: false
iOS:
  hermesEnabled: Not found
  newArchEnabled: Not found

The remote server:

System:
    OS: Linux 5.4 Ubuntu 20.04.6 LTS (Focal Fossa)
    CPU: (2) x64 Intel(R) Xeon(R) CPU E5-2687W v4 @ 3.00GHz
    Memory: 1.83 GB / 3.83 GB
    Shell: 5.0.17 - /bin/bash
  Binaries:
    Node: 18.20.4 - ~/.nvm/versions/node/v18.20.4/bin/node
    Yarn: Not Found
    npm: 10.7.0 - ~/.nvm/versions/node/v18.20.4/bin/npm
    Watchman: Not Found
  SDKs:
    Android SDK: Not Found
  IDEs:
    Android Studio: Not Found
  Languages:
    Java: Not Found
  npmPackages:
    @react-native-community/cli: Not Found
    react: 18.3.1 => 18.3.1
    react-native: 0.75.3 => 0.75.3
  npmGlobalPackages:
    *react-native*: Not Found

Platforms

iOS

Versions

  • Android: -
  • iOS: 17.6.1
  • react-native-geolocation: 3.4.0
  • react-native: 0.75.3
  • react: 13.3.1

Description

On iOS the .requestAuthorization seems to do nothing until you go into the settings and switch the location permission then suddenly all pending requests are returned in success/error.
Android required me to set locationProvider to playServices but no matter what I try for the iOS configurations I can't get it to call a function normally.

Reproducible Demo

This is the exact code i use in our project and in a clean project. (with moment.js installed for timing)

Info.plist has this key NSLocationWhenInUseUsageDescription but no value

// this seems to work fine on android
Geolocation.setRNConfiguration({
    locationProvider:   'playServices',
    authorizationLevel: 'whenInUse'
});

let moment_object = moment();

// this gets called
console.log('Geolocation.requestAuthorization', Geolocation.requestAuthorization, moment_object.format('HH:mm:ss'));

// success or error only gets called when you go into the iOS settings and switch the location permissions
// otherwise this just hangs in limbo
Geolocation.requestAuthorization(() => { // success callback
    console.log('Geolocation.requestAuthorization', true, moment_object.format('HH:mm:ss'), moment().format('HH:mm:ss'));
}, () => { // error callback
    console.log('Geolocation.requestAuthorization', false, moment_object.format('HH:mm:ss'), moment().format('HH:mm:ss'));
});
@crasyboy42 crasyboy42 added the bug Something isn't working label Oct 2, 2024
@stealkiller06
Copy link

Don't use that for checking the permission, use this instead:

package:https://www.npmjs.com/package/react-native-permissions

export const requestAuthorization = async (): Promise<boolean> => {
  const permission =
    Platform.OS === 'android'
      ? PERMISSIONS.ANDROID.ACCESS_FINE_LOCATION
      : PERMISSIONS.IOS.LOCATION_WHEN_IN_USE;

  try {
    const result = await check(permission);
    if (result === RESULTS.GRANTED) {
      return true;
    } else if (result === RESULTS.DENIED || result === RESULTS.BLOCKED) {
      const requestResult = await request(permission);
      return requestResult === RESULTS.GRANTED;
    } else {
      return false;
    }
  } catch (err) {
    console.warn(err);
    return false;
  }
};

@GGraciet
Copy link

GGraciet commented Nov 25, 2024

The issue above still exists, I just runned into it today. Same as described, it looks like Geolocation.requestAuthorization doesn't call any of its callbacks on iOS.

Even if @stealkiller06 solution works, I don't think that "use another package to do it" is a valid solution. It just adds an extra redundent dependency instead of fixing the real issue.. If this method exists, it must be usable (and used).


EDIT: Explanation found

After digging up a little bit in the module iOS native code, if found this :

In RNCGeolocation.mm, line 284 :

  // Request location access permission
  if (wantsAlways) {
#if !TARGET_OS_VISION
    [_locationManager requestAlwaysAuthorization];
    [self enableBackgroundLocationUpdates];
#endif
  } else if (wantsWhenInUse) {
    [_locationManager requestWhenInUseAuthorization];
  }
}

What's going on ?

  • wantsAlways is true when "NSLocationAlwaysUsageDescription" has a value in the Info.plist file.
  • If wantsAlways is true, then enableBackgroundLocationUpdates is called.
  • If you followed the installation instructions, you have probably set both "NSLocationAlwaysUsageDescription" and "NSLocationWhenInUseUsageDescription"
  • But, if you didn't intended to use the background location feature, you didn't setup the corresponding Capability (see docs).

And so, what is the problem with this ?
As the native code checks for "NSLocationAlwaysUsageDescription" first, it will always enter the first condition block and call enableBackgroundLocationUpdateswhich is unable to process correctly as the capability isn't added to the app. So your request is kind of soft locked.

How to fix this?

Solution 1 : Explicitly set the config to force the authorizationLevel to "whenInUse".

Geolocation.setRNConfiguration({
    skipPermissionRequests: false,
    authorizationLevel: 'whenInUse',
})

Solution 2 : Add the background location capability (see: https://www.npmjs.com/package/@react-native-community/geolocation#requestauthorization:~:text=In%20order%20to%20enable%20geolocation%20in%20the%20background%2C%20you%20need%20to%20include%20the%20%27NSLocationAlwaysUsageDescription%27%20key%20in%20Info.plist%20and%20add%20location%20as%20a%20background%20mode%20in%20the%20%27Capabilities%27%20tab%20in%20Xcode.)

Conclusion
I my opinion, it doesn't deserve any fix, as the "always" mode is useful only when background location is needed. But, adding a caveat to the setup docs could be a good idea.


**EDIT 2: iOS native requestWhenInUseAuthorization() behavior **

@crasyboy42 Your issue is more probably caused by this.

The iOS native docs for requestAlwaysAuthorization() tells us :

After the user makes a selection and determines the status, the location manager delivers the results to the delegate’s [locationManager(_:didChangeAuthorization:)](https://developer.apple.com/documentation/corelocation/cllocationmanagerdelegate/locationmanager(_:didchangeauthorization:)) method. If the initial authorization status is anything other than [CLAuthorizationStatus.notDetermined](https://developer.apple.com/documentation/corelocation/clauthorizationstatus/notdetermined), this method does nothing and doesn’t call the [locationManager(_:didChangeAuthorization:)](https://developer.apple.com/documentation/corelocation/cllocationmanagerdelegate/locationmanager(_:didchangeauthorization:)) method.

It means that, on the same run, calling Geolocation.requestAuthorization more than one time will not work, as the "authorization status is anything other than [CLAuthorizationStatus.notDetermined]".

My temporary solution is to fix the package (eg. using patch-package) by replacing line 291 in RNCGeolocation.mm with :

if(_lastUpdatedAuthorizationStatus == kCLAuthorizationStatusNotDetermined) {
  [_locationManager requestWhenInUseAuthorization];
} else {
  [self locationManagerDidChangeAuthorization:_locationManager];
}

I'll open a PR with this soon, I think its a better behavior as the module doesn't offer any way to check the CLAuthorizationStatus value.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants