-
-
Notifications
You must be signed in to change notification settings - Fork 828
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: Cannot request location always if we already have location when in use permission #490
Comments
@tallpants This library already handle location permission escalation. The line you give is from the A normal permission escalation would be:
|
@zoontek doing |
I just checked it, it's not that easy to fix it. I explain myself: |
You're right yea it's more complicated than I initially thought. Do you have any ideas on how to approach fixing it? My initial guess is that without some form of persistence (maybe to Something like this (pseudocode -- doesn't handle all the other cases): - (void)checkWithResolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
CLAuthorizationStatus authorizationStatus = [CLLocationManager authorizationStatus];
BOOL hasRequestedLocationAlways = [NSUserDefaults standardDefaults] boolForKey:@"RNPermissionHandlerLocationAlways-hasRequested"];
if (authorizationStatus == kCLAuthorizationStatusAuthorizedWhenInUse && hasRequestedLocationAlways) {
return resolve(RNPermissionStatusDenied);
} else {
return resolve(RNPermissionStatusNotDetermined);
}
} ... and similary setting the Just an idea of course, maybe you can find a nicer way of solving. |
The core already has such helper method (https://github.com/react-native-community/react-native-permissions/blob/master/ios/RNPermissions.m#L324) It might work, but we have to flag as requested only if the result isn't |
Would love the ability to call requestAlwaysAuthorization twice to get to Always faster too. |
This is under discussion here as well: https://github.com/transistorsoft/react-native-background-geolocation-android/issues/938#issuecomment-668042805 By you @rolfb :-), just cross-linking here as you've got great research posted in the other issue |
Cheers, @mikehardy! The other one is private though :-) |
Ah true enough - it affects the public version, but either way may result in knowledge here :) |
It seems like you have to explicitly request WhenInUse first, then you can request Always directly after. If you request Always first and get While in use (as read from device settings) you can't request Always to trigger the "Change to always" alert and will have to wait for the system to trigger it. This is probably clear to most, just wanted to summarize. |
For reference, the background-geolocation-android private repo owner @christocracy posted this as a solution:
I have tested by commenting-out the check for |
@jdegger that'll work for the initial request -- but to keep the API consistent we also need to track manually when we've asked for it already and return |
That's correct. The updated logic in Note that if user resets location authorization to BOOL isRequestUpgradeAlways = NO;
if (status == kCLAuthorizationStatusNotDetermined) {
// Reset upgrade-to-always state in UserDefaults
config.didRequestUpgradeLocationAuthorization = NO;
} else if ((status == kCLAuthorizationStatusAuthorizedWhenInUse) && ([desiredRequest isEqualToString:ALWAYS_STRING]) && !config.didRequestUpgradeLocationAuthorization ) {
isRequestUpgradeAlways = YES;
}
if ((status == kCLAuthorizationStatusNotDetermined) || isRequestUpgradeAlways ) {
// Update upgrade-to-always state in UserDefaults
if (isRequestUpgradeAlways) {
config.didRequestUpgradeLocationAuthorization = YES;
}
// Request authorization
.
.
.
} |
Is there any update on this issue? |
I personally don't have any time to work on open source these days. |
@zoontek I'm the author of react-native-background-geolocation. Requesting
/// Returns true if the Dialog should be shown
@TargetApi(29)
public boolean shouldShow(Activity activity) {
if (activity == null || misDialogActive.get()) return false;
return ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.ACCESS_BACKGROUND_LOCATION);
}
backgroundPermissionRationale: {
title: "Allow access to this device's location in the background?",
message: "In order to X, Y and Z, please enable 'Allow all the time permission",
okButton: "Change to Allow all the time"
} In the |
@zoontek Maybe another way to handle this without your library producing a native Android |
@christocracy I personally hate apps that request Thanks for the informations, I will check this when I will have some time (we just launched our product, I'm really busy these days 😅). I'll try to choose the easiest solution, but I'm afraid of these changes. |
@zoontek I agree with you in general and in spirit, however I have one of those apps that nevertheless does something useful with the always permission, in a transparent + delete-able and even optional way, such that if it's allowed it could help the user, but we respect that people hate it (since normally they receive no value for their data). Which is just to say, sure, but...sometimes there's reasons, so I'd love to see the feature. Recognizing I may need to code it up :-), although I'm pretty busy myself at the moment |
My plugin was originally designed for tracking first-responders in disaster-zones (hurricanes, earthquakes), where life depended on it. It is ideally suited for fleet-tracking use-cases (eg: Jogging App, Delivery service app, Taxi / Trucking apps). I make no moral judgements on my customers' usage of Always authorization though I do let those who I find using it in Social app that it's not a good idea. I welcome these strict new permission authorization mechanisms introduced by both Google and iOS. |
@christocracy @mikehardy Guys it's not against you, I also worked on several apps that required background geolocation, it's just that I see this feature abused :) I will try to add a light API to handle these cases, but not an ultra complete one with X fancy options 😅 |
@zoontek would you be willing to accept a PR that fixes only the iOS permission escalation issue (where you can't request location always authorization after you've been granted location when in use authorization) -- which is what the original issue was about, and then we can track the problems on Android and a new API for extra cases and so on in a different issue? I'm hoping we can merge it and release a minor version since it's technically a bug with the library, instead of saving it for 3.0. |
PR opened for this here: #529 |
I have ran into this issue as well. We have two use-cases in our app, one that only needs whenInUse auth, and another, rare use-case that requires always. Apple's own guidelines state,
Therefore this current bug does not allow us React Native users to follow Apple's guidelines. I acknowledge that @zoontek doesn't have time to prioritize this issue right now, but wanted to make a note here. |
@taylorkline we'd all like to see this fixed but for now you'll have to consider this a missing feature. I'd suggest using the changes in my PR linked above along with patch-package in the meantime. Realistically I think this'll only be fixed in the next major release because it's not possible to fit this permission into the existing API. |
Hi guys, I've came up with possible solution proposed here in @tallpants PR index 29c9075..c7e2f7e 100644
--- a/node_modules/react-native-permissions/ios/LocationAlways/RNPermissionHandlerLocationAlways.m
+++ b/node_modules/react-native-permissions/ios/LocationAlways/RNPermissionHandlerLocationAlways.m
@@ -35,7 +35,13 @@ - (void)checkWithResolver:(void (^ _Nonnull)(RNPermissionStatus))resolve
return resolve(RNPermissionStatusNotDetermined);
case kCLAuthorizationStatusRestricted:
return resolve(RNPermissionStatusRestricted);
- case kCLAuthorizationStatusAuthorizedWhenInUse:
+ case kCLAuthorizationStatusAuthorizedWhenInUse: {
+ BOOL requestedBefore = [RNPermissions isFlaggedAsRequested:[[self class] handlerUniqueId]];
+ if (requestedBefore) {
+ return resolve(RNPermissionStatusDenied);
+ }
+ return resolve(RNPermissionStatusNotDetermined);
+ }
case kCLAuthorizationStatusDenied:
return resolve(RNPermissionStatusDenied);
case kCLAuthorizationStatusAuthorizedAlways:
@@ -48,7 +54,11 @@ - (void)requestWithResolver:(void (^ _Nonnull)(RNPermissionStatus))resolve
if (![CLLocationManager locationServicesEnabled]) {
return resolve(RNPermissionStatusNotAvailable);
}
- if ([CLLocationManager authorizationStatus] != kCLAuthorizationStatusNotDetermined) {
+
+ CLAuthorizationStatus authorizationStatus = [CLLocationManager authorizationStatus];
+ BOOL requestedBefore = [RNPermissions isFlaggedAsRequested:[[self class] handlerUniqueId]];
+
+ if (authorizationStatus != kCLAuthorizationStatusNotDetermined && (authorizationStatus == kCLAuthorizationStatusAuthorizedWhenInUse && requestedBefore)) {
return [self checkWithResolver:resolve rejecter:reject];
}
@@ -57,14 +67,35 @@ - (void)requestWithResolver:(void (^ _Nonnull)(RNPermissionStatus))resolve
_locationManager = [CLLocationManager new];
[_locationManager setDelegate:self];
+
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(handleAppStateDidChange:)
+ name:UIApplicationDidBecomeActiveNotification
+ object:nil];
+
[_locationManager requestAlwaysAuthorization];
+ [RNPermissions flagAsRequested:[[self class] handlerUniqueId]];
}
-- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status {
- if (status != kCLAuthorizationStatusNotDetermined) {
- [_locationManager setDelegate:nil];
+- (void)updatePermission {
+ if(_resolve == nil || _reject == nil) return;
[self checkWithResolver:_resolve rejecter:_reject];
- }
+ [_locationManager setDelegate:nil];
+ _resolve = nil;
+ _reject = nil;
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidBecomeActiveNotification object:nil];
+}
+
+- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status {
+ if (status != kCLAuthorizationStatusNotDetermined && status != kCLAuthorizationStatusAuthorizedWhenInUse) {
+ [self updatePermission];
+ }
+}
+
+- (void)handleAppStateDidChange:(NSNotification *)notification {
+ if([notification.name isEqualToString:UIApplicationDidBecomeActiveNotification]) {
+ [self updatePermission];
+ }
}
@end |
@zoontek we're revisiting this at our org -- the PR I made for this was left hanging because of your concern over a promise that might be unresolved indefinitely (which was definitely not ideal I agree). We're willing to do the work to switch to the |
@tallpants Yes, that could do the trick! |
ping @zoontek -- just following up on this! |
Just fyi, the guys of Expo can do this job with their package location, maybe you can look how they did it https://github.com/expo/expo/tree/master/packages/expo-location |
Bug report
If we have location when in use permission, requesting location always permission always returns denied, probably because of this line: https://github.com/react-native-community/react-native-permissions/blob/4017cbf0861b562bb48888f76cfa7c9a62c68ffc/ios/LocationAlways/RNPermissionHandlerLocationAlways.m#L38
Apple's documentation is pretty clear about how you're allowed to call
requestAlwaysAuthorization
if the current authorization status is eitherkCLAuthorizationStatusNotDetermined
orkCLAuthorizationStatusAuthorizedWhenInUse
. Calling this when we already have when-in-use authorization will prompt the user to upgrade to "always allow" location permissions.But in the library we're assuming that if we currently have when-in-use authorization, we're not allowed to ask for always authorization.
I tested by manually calling
requestAlwaysAuthorization
in Objective-C after getting when-in-use authorization fromreact-native-permissions
-- and the upgrade prompt shows up just fine.This should be an easy fix (as far as I can tell).
Documentation for
requestAlwaysAuthorization
that explains this: https://developer.apple.com/documentation/corelocation/cllocationmanager/1620551-requestalwaysauthorization?changes=latest_minor&language=objcUpvote & Fund
The text was updated successfully, but these errors were encountered: