zoontek / react-native-permissions

An unified permissions API for React Native on iOS, Android and Windows.
MIT License
4.11k stars 837 forks source link

iOS: check location permission returns 'blocked' when permission is granted but location services are disabled #852

Closed fikkatra closed 9 months ago

fikkatra commented 9 months ago

Before submitting a new issue

Bug summary

We upgraded from v3.9.2 to v4.1.4 and noticed a change in behavior when checking location permissions on iOS.

When the user granted location permissions to the app, but location services are disabled for the entire device, check(PERMISSIONS.IOS.LOCATION_WHEN_IN_USE) returns blocked.

In prior versions, this used to be unavailable, which makes more sense imo.

The current behavior makes it impossible to make a distinction between permissions not being granted, and location services being disabled completely.

Library version

4.1.4

Environment info

System:
  OS: macOS 14.2.1
  CPU: (8) arm64 Apple M1 Pro
  Memory: 132.23 MB / 32.00 GB
  Shell:
    version: "5.9"
    path: /bin/zsh
Binaries:
  Node:
    version: 18.15.0
    path: /usr/local/bin/node
  Yarn:
    version: 1.22.19
    path: /usr/local/bin/yarn
  npm:
    version: 9.5.0
    path: /usr/local/bin/npm
  Watchman:
    version: 2024.01.22.00
    path: /opt/homebrew/bin/watchman
Managers:
  CocoaPods:
    version: 1.15.2
    path: /Users/sophietraen/.rbenv/shims/pod
SDKs:
  iOS SDK:
    Platforms:
      - DriverKit 23.2
      - iOS 17.2
      - macOS 14.2
      - tvOS 17.2
      - visionOS 1.0
      - watchOS 10.2
  Android SDK:
    API Levels:
      - "31"
      - "33"
      - "34"
    Build Tools:
      - 30.0.3
      - 33.0.0
      - 33.0.1
      - 33.0.2
      - 34.0.0
    System Images:
      - android-27 | Google APIs ARM 64 v8a
      - android-30 | Google APIs ARM 64 v8a
      - android-31 | Google APIs ARM 64 v8a
      - android-31 | Google Play ARM 64 v8a
      - android-33 | Google APIs ARM 64 v8a
      - android-33 | Google Play ARM 64 v8a
      - android-34 | Google APIs ARM 64 v8a
    Android NDK: Not Found
IDEs:
  Android Studio: 2023.1 AI-231.9392.1.2311.11330709
  Xcode:
    version: 15.2/15C500b
    path: /usr/bin/xcodebuild
Languages:
  Java:
    version: 17.0.10
    path: /usr/bin/javac
  Ruby:
    version: 2.7.6
    path: /Users/sophietraen/.rbenv/shims/ruby
npmPackages:
  "@react-native-community/cli": Not Found
  react:
    installed: 18.2.0
    wanted: 18.2.0
  react-native:
    installed: 0.73.5
    wanted: 0.73.5
  react-native-macos: Not Found
npmGlobalPackages:
  "*react-native*": Not Found
Android:
  hermesEnabled: true
  newArchEnabled: false
iOS:
  hermesEnabled: true
  newArchEnabled: false

Steps to reproduce

  1. Request & grant location permissions to the app (PERMISSIONS.IOS.LOCATION_WHEN_IN_USE)
  2. Disable location services for the entire device (through Settings -> Privacy & Security -> Location Services -> toggle)
  3. Check permissions: check(PERMISSIONS.IOS.LOCATION_WHEN_IN_USE)

Result: 'blocked'. Expected result (observed in previous versions): 'unavailable' Using real device iPhone 13 Pro

Reproducible sample code

* request(PERMISSIONS.IOS.LOCATION_WHEN_IN_USE)
* check(PERMISSIONS.IOS.LOCATION_WHEN_IN_USE)
zoontek commented 9 months ago

This is intentional, as this is the state of the permission, not of the feature + the behavior is unified with android now.

If you use a library to get the user geolocation, you should be able to perform this check with it.

fikkatra commented 9 months ago

I do appreciate the efforts of having unified behavior with Android. However, I don't think it is in fact unified? In the scenario where permissions are given, but GPS is disabled, a check on Android returns 'granted' or 'denied' depending on the permissions given. On iOS, we receive 'blocked', regardless of the permissions given. So it's not exactly unified behavior.

This has an impact on deriving whether GPS is disabled or not. For Android, in case of 'granted', if we don't receive actual gps coordinates or we get an error, we can conclude the GPS is disabled.

In case of iOS, when receiving 'blocked' there is no way of knowing the reason (either missing permissions, or GPS disabled). A library to get the user geolocation will return an error in both cases.

You are correct that disabling the GPS has nothing to do with permissions. However, in that case I'd expect check to return something like null or 'unknown' (or 'granted', 'denied' or 'blocked', depending on actual permissions given). This would indicate the permission state more clearly, imo.

zoontek commented 9 months ago

It's unified in the way we now don't check location services statuses before checking permissions on both platforms.

iOS choose to return blocked, android to return denied / granted.

The key is to not start the GPS, but to check if location services is on:

fikkatra commented 9 months ago

Ok, that seems indeed logical. Thanks for taking the time to explain!

johhansantana commented 7 months ago

@fikkatra how did you solve the issue to differentiate between the 2 (location services vs app specific permission)

fikkatra commented 7 months ago

@johhansantana for the status of the location services, we ended up using DeviceInfo.isLocationEnabled() from react-native-device-info.