React-native: navigator.geolocation.getCurrentPosition stop working after we add NSLocationAlwaysUsageDescription and NSLocationWhenInUseUsageDescription to plist.

Created on 27 Apr 2018  路  27Comments  路  Source: facebook/react-native

Environment


Environment:
OS: macOS High Sierra 10.13.4
Node: 8.11.1
Yarn: 1.6.0
npm: Not Found
Watchman: 4.9.0
Xcode: Xcode 9.3 Build version 9E145
Android Studio: 3.0 AI-171.4443003

Packages: (wanted => installed)
react: 16.2.0 => 16.2.0
react-native: 0.53.0 => 0.53.0

Steps to Reproduce

@tarikkurucu and me are developing an application with pure react-native (not with expo) and yesterday we deploy our new version of application to App Store in order to test in TestFlight. after application uploaded to store we received a letter from Apple that we must add 2 keys to our .plist file as below:

We identified one or more issues with a recent delivery for your app, "***". Please correct the following issues, then upload again.

Missing Info.plist key - This app attempts to access privacy-sensitive data without a usage description. The app's Info.plist must contain an NSLocationAlwaysUsageDescription key with a string value explaining to the user how the app uses this data.

Missing Info.plist key - This app attempts to access privacy-sensitive data without a usage description. The app's Info.plist must contain an NSLocationWhenInUseUsageDescription key with a string value explaining to the user how the app uses this data.

After we add keys to .plist file we figured out that navigator.geolocation.getCurrentPosition stopped working.

After searching in Internet we couldn't find any solution and finally we write our native module.

Expected Behavior

navigator.geolocation.getCurrentPosition must return user location after user allows to reach GPS.

Actual Behavior


navigator.geolocation.getCurrentPosition not fire up after call.

NativeLocationRequest.h:

  #import <React/RCTBridgeModule.h>
  @interface NativeLocationRequest : NSObject<RCTBridgeModule>
   @end

NativeLocationRequest.m:

#import "NativeLocationRequest.h"
#import <CoreLocation/CoreLocation.h>

@implementation NativeLocationRequest
RCT_EXPORT_MODULE();

CLLocationManager *locationManager;


RCT_EXPORT_METHOD(NativeLocationRequestStart)
{

  if ([CLLocationManager locationServicesEnabled]) {

    switch ([CLLocationManager authorizationStatus]) {
      case kCLAuthorizationStatusDenied:
        locationManager = [[CLLocationManager alloc] init];
        locationManager.desiredAccuracy = kCLLocationAccuracyBest;
        //[locationManager requestAlwaysAuthorization];
        [locationManager requestWhenInUseAuthorization];

        break;
      case kCLAuthorizationStatusRestricted:

        locationManager = [[CLLocationManager alloc] init];
        locationManager.desiredAccuracy = kCLLocationAccuracyBest;
        //[locationManager requestAlwaysAuthorization];
        [locationManager requestWhenInUseAuthorization];

        break;
      case kCLAuthorizationStatusAuthorizedAlways:

        break;
      case kCLAuthorizationStatusAuthorizedWhenInUse:

        break;
      case kCLAuthorizationStatusNotDetermined:
        locationManager = [[CLLocationManager alloc] init];
        locationManager.desiredAccuracy = kCLLocationAccuracyBest;
        //[locationManager requestAlwaysAuthorization];
        [locationManager requestWhenInUseAuthorization];
        break;
      default:
        break;
    }
  } else {
    locationManager = [[CLLocationManager alloc] init];
    locationManager.desiredAccuracy = kCLLocationAccuracyBest;
    //[locationManager requestAlwaysAuthorization];
    [locationManager requestWhenInUseAuthorization];

  }

}
@end
iOS Locked

Most helpful comment

Had the same problem, got it working by adding all 3 keys in Info.plist:

    <key>NSLocationWhenInUseUsageDescription</key>
    <string>Enabling location copy - in use</string>
    <key>NSLocationAlwaysUsageDescription</key>
    <string>Enabling location copy - always</string>
    <key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
    <string>Enabling location copy - both</string>

According to the docs here: https://developer.apple.com/documentation/corelocation/choosing_the_authorization_level_for_location_services/requesting_always_authorization

You are required to include the NSLocationWhenInUseUsageDescription and NSLocationAlwaysAndWhenInUseUsageDescription keys in your app's Info.plist file. (If your app supports iOS 10 and earlier, the NSLocationAlwaysUsageDescription key is also required.) If those keys are not present, authorization requests fail immediately.

All 27 comments

Thanks for posting this! It looks like your issue may refer to an older version of React Native. Can you reproduce the issue on the latest release, v0.55?

Thank you for your contributions.

Same result with fresh react-native init

Environment:
OS: macOS High Sierra 10.13.4
Node: 8.11.1
Yarn: 1.6.0
npm: 6.0.0
Watchman: 4.9.0
Xcode: Xcode 9.3 Build version 9E145
Android Studio: 3.0 AI-171.4443003

Packages: (wanted => installed)
react: 16.3.1 => 16.3.1
react-native: 0.55.3 => 0.55.3

I'm having the same issue after upgrading to RN 0.55.4
Without NSLocationAlwaysUsageDescription in Info.plist, rejected by Apple
With NSLocationAlwaysUsageDescription in Info.plist, navigator.geolocation.getCurrentPosition doesn't work

I think this is a recent change in Apple's binary submission checks, because after downgrading to 0.54.0, my builds are still being rejected for not including NSLocationAlwaysUsageDescription inInfo.plist`. Even though I successfully submitted a binary on RN 0.54.0 a few days ago

Can confirm my RN 0.54.0 build was accepted in iTunes Connect with the following temporary change to node_modules/react-native/Libraries/Geolocation/RCTLocationObserver.m:

Before:

if ([[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationAlwaysUsageDescription"] &&
    [_locationManager respondsToSelector:@selector(requestAlwaysAuthorization)]) {
    [_locationManager requestAlwaysAuthorization];

    // On iOS 9+ we also need to enable background updates
    NSArray *backgroundModes  = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UIBackgroundModes"];
    if (backgroundModes && [backgroundModes containsObject:@"location"]) {
      if ([_locationManager respondsToSelector:@selector(setAllowsBackgroundLocationUpdates:)]) {
        [_locationManager setAllowsBackgroundLocationUpdates:YES];
      }
    }
  } else if ([[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationWhenInUseUsageDescription"] &&
    [_locationManager respondsToSelector:@selector(requestWhenInUseAuthorization)]) {
    [_locationManager requestWhenInUseAuthorization];
  }

After:

// Request location access permission
  /*if ([[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationAlwaysUsageDescription"] &&
    [_locationManager respondsToSelector:@selector(requestAlwaysAuthorization)]) {
    [_locationManager requestAlwaysAuthorization];

    // On iOS 9+ we also need to enable background updates
    NSArray *backgroundModes  = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UIBackgroundModes"];
    if (backgroundModes && [backgroundModes containsObject:@"location"]) {
      if ([_locationManager respondsToSelector:@selector(setAllowsBackgroundLocationUpdates:)]) {
        [_locationManager setAllowsBackgroundLocationUpdates:YES];
      }
    }
  } else */if ([[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationWhenInUseUsageDescription"] &&
    [_locationManager respondsToSelector:@selector(requestWhenInUseAuthorization)]) {
    [_locationManager requestWhenInUseAuthorization];
  }

Same problem. @sfyfe31 Thanks for your solution but is any workaround other than overwriting the RCTLocationObserver.m ?

@uuau99999 I preferred to get user location by hand made native component (NativeLocationRequest that I defined above) until react-native fix this.

@sfyfe31 Thank you! That worked for me.

Had the same problem, got it working by adding all 3 keys in Info.plist:

    <key>NSLocationWhenInUseUsageDescription</key>
    <string>Enabling location copy - in use</string>
    <key>NSLocationAlwaysUsageDescription</key>
    <string>Enabling location copy - always</string>
    <key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
    <string>Enabling location copy - both</string>

According to the docs here: https://developer.apple.com/documentation/corelocation/choosing_the_authorization_level_for_location_services/requesting_always_authorization

You are required to include the NSLocationWhenInUseUsageDescription and NSLocationAlwaysAndWhenInUseUsageDescription keys in your app's Info.plist file. (If your app supports iOS 10 and earlier, the NSLocationAlwaysUsageDescription key is also required.) If those keys are not present, authorization requests fail immediately.

The problem isn't with 0.54.0. The changes made in #5093 coupled with new Apple requirements are the issue. The PR comment states "If NSLocationAlwaysUsageDescription is set, NSLocationWhenInUseUsageDescription will be simply ignored". Like @sadeghipour, I received a warning email from Apple for missing keys; it stated:

Starting spring 2019, all apps submitted to the App Store that access user data will be required to include a purpose string.If you're using external libraries or SDKs, they may reference APIs that require a purpose string. While your app might not use these APIs, a purpose string is still required. You can contact the developer of the library or SDK and request they release a version of their code that doesn't contain the APIs.

Additionally, including the NSLocation keys stated above means you must also include NSLocationAlwaysAndWhenInUseUsageDescription with a usage description in order for location fetching to work.

So there's your answer right there and would explain why commenting out objectForInfoDictionaryKey:@"NSLocationAlwaysUsageDescription"] as @sfyfe31 has done would result in a successful App Store Connect submission.

From what I can tell, if your app is only meant to get a user's location while the app is in use, you can include only NSLocationWhenInUseDescription, make @sfyfe31's change (copied below, but with syntax highlighting), and perhaps look into patch-package. Otherwise, you'll have to include all 3 NSLocation keys mentioned in this comment, as @arosca has done.

react-native/Libraries/Geolocation/RCTLocationObserver.m

  // Request location access permission
  /*if ([[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationAlwaysUsageDescription"] &&
    [_locationManager respondsToSelector:@selector(requestAlwaysAuthorization)]) {
    [_locationManager requestAlwaysAuthorization];

    // On iOS 9+ we also need to enable background updates
    NSArray *backgroundModes  = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UIBackgroundModes"];
    if (backgroundModes && [backgroundModes containsObject:@"location"]) {
      if ([_locationManager respondsToSelector:@selector(setAllowsBackgroundLocationUpdates:)]) {
        [_locationManager setAllowsBackgroundLocationUpdates:YES];
      }
    }
  } else */if ([[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationWhenInUseUsageDescription"] &&
    [_locationManager respondsToSelector:@selector(requestWhenInUseAuthorization)]) {
    [_locationManager requestWhenInUseAuthorization];
  }

I have the same problem, but I'm not using location services in my app. Does expo (RN) use Location Services by default? If yes, how can I disable it?

@palisand

Unfortunately the is problem continues and not fixed yet.

https://forums.expo.io/t/rejected-from-app-store-missing-location-string/1302

====update====
I rechecked the email that I receive from apple and not sure that not any part forget to paste in here.

===update====
Oops, I missed last post that confirm the problem has been solved.

===update====

link updated:
https://forums.expo.io/t/rejected-from-app-store-missing-location-string/13029/5

@sadeghipour I don't appear to have access to that forum post, what does it say?

Edit: Thanks!

@Palisand Given the code in this section, would adding the purpose string mean wasteful behaviour would start occurring?

@sadeghipour I've updated my first comment in light of you having verified the contents of the email you received.

@tclarke-scottlogic I'm not exactly sure what you're asking. If you leave react-native untouched and do not want to receive a warning email from Apple, a purpose string is required for the NSLocation* keys present in the section you posted (which I had included a previous comment). However, failing to provide the string apparently does not result in an app rejection (see @sadeghipour latest comment). If by wasteful behavior you mean including an extra Always permission rather than just When In Use, that should depend on what iOS version you're running and the inclusion of NSLocationAlwaysAndWhenInUseUsageDescription. I suggest that if you not using expo, are working on a shared codebase, have not forked or don't want to fork react-native, and all you want to do is access a user's location only while that user is using your app, you should use patch-package and update _RCTLocationObserver.m_.

@Palisand
if we need only 'when use' location
use 2 keys in info.plist:
<key>NSLocationWhenInUseUsageDescription</key> <string>message</string> <key>NSLocationAlwaysAndWhenInUsageDescription</key> <string>message</string>
it passes apple review.
you doent need <key>NSLocationAlwaysUsageDescription</key> this key is why Geolocation fails

@Palisand have you tried this with an app supporting iOS 10 in the appstore yet? What you say seems to conflict with the Apple documentation (emphasis added):

Important
You are required to include the NSLocationWhenInUseUsageDescription and NSLocationAlwaysAndWhenInUseUsageDescription keys in your app's Info.plist file. (If your app supports iOS 10 and earlier, the NSLocationAlwaysUsageDescription key is also required.) If those keys are not present, authorization requests fail immediately.

Had the same problem, got it working by adding all 3 keys in Info.plist:

    <key>NSLocationWhenInUseUsageDescription</key>
    <string>Enabling location copy - in use</string>
    <key>NSLocationAlwaysUsageDescription</key>
    <string>Enabling location copy - always</string>
    <key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
    <string>Enabling location copy - both</string>

According to the docs here: https://developer.apple.com/documentation/corelocation/choosing_the_authorization_level_for_location_services/requesting_always_authorization

You are required to include the NSLocationWhenInUseUsageDescription and NSLocationAlwaysAndWhenInUseUsageDescription keys in your app's Info.plist file. (If your app supports iOS 10 and earlier, the NSLocationAlwaysUsageDescription key is also required.) If those keys are not present, authorization requests fail immediately.

this way works for me! thank you !

Uhm given that is seems that the solution is to add those string to the info.plist, do you feel that we can close this issue? Any reason why this should stay open? 馃

@arosca Thank you so much!

@Palisand
if we need only 'when use' location
use 2 keys in info.plist:
<key>NSLocationWhenInUseUsageDescription</key> <string>message</string> <key>NSLocationAlwaysAndWhenInUsageDescription</key> <string>message</string>
it passes apple review.
you doent need <key>NSLocationAlwaysUsageDescription</key> this key is why Geolocation fails

Wow this actually solved it for me!

Adding all 3 keys, as @arosca mentioned, works for me. The documentation however only instructs new users to add NSLocationWhenInUseUsageDescription to the Info.plist:

https://facebook.github.io/react-native/docs/0.59/geolocation

Would be nice to have the official documentation reflect the correct way to get started with geolocation. (@kelset?)

I guess that you could submit a PR for that? Btw in case you missed, geolocation now lives in its own repo -> https://github.com/react-native-community/react-native-geolocation

@kelset Ah, totally missed that. Submitted a PR, thanks!

no thank you :3

Was this page helpful?
0 / 5 - 0 ratings