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

[Bug]: Apple complains about the need for NSLocationAlwaysAndWhenInUseUsageDescription #1403

Closed
3 of 8 tasks
781flyingdutchman opened this issue Jan 4, 2024 · 9 comments
Closed
3 of 8 tasks
Labels
P2 Important issues not at the top of the work list. type: enhancement New feature or request

Comments

@781flyingdutchman
Copy link
Contributor

781flyingdutchman commented Jan 4, 2024

Please check the following before submitting a new issue.

Please select affected platform(s)

  • Android
  • iOS
  • Linux
  • macOS
  • Web
  • Windows

Steps to reproduce

I have the NSLocationWhenInUseUsageDescription set, and don't want/expect my user to grant 'always' permission, so I have not set NSLocationAlwaysAndWhenInUseUsageDescription.

Expected results

No issue with Apple submission

Actual results

Apple rejects the app, because the plugin code in PermissionHandler.m line 76 contains a reference to the 'always' authorization [locationManager requestAlwaysAuthorization];, even though I never request that (because NSLocationAlwaysAndWhenInUseUsageDescription is not set).

You may have to implement an approach where users add a compile constant to the PodFile that allows you to conditionally compile the request for 'always' authorization (using #if, not if). If it's not compiled, Apple won't complain.

See here for how I've done this for a plugin I maintain. (commit)

Code sample

N/A

Screenshots or video

No response

Version

10.1.0

Flutter Doctor output

Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 3.16.5, on macOS 14.2.1 23C71 darwin-arm64, locale en-US)
[✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
[✓] Xcode - develop for iOS and macOS (Xcode 15.0.1)
[✓] Chrome - develop for the web
[✓] Android Studio (version 2023.1)
[✓] Connected device (4 available)
[✓] Network resources

• No issues found!

@781flyingdutchman 781flyingdutchman changed the title [Bug]: [Bug]: Apple complains about the need for NSLocationAlwaysAndWhenInUseUsageDescription Jan 4, 2024
@TimHoogstrate
Copy link
Contributor

Dear @781flyingdutchman,

Thanks for your input. I'll label this issue as an enhancement to keep track of it.

Kind regards,

@TimHoogstrate TimHoogstrate added type: enhancement New feature or request P2 Important issues not at the top of the work list. labels Jan 4, 2024
781flyingdutchman added a commit to 781flyingdutchman/flutter-geolocator that referenced this issue Jan 5, 2024
…for 'always' permission, to prevent users getting rejected by Apple for not including NSLocationAlwaysAndWhenInUseUsageDescription even if they never require background location updates.

The compile is conditional on the flag PERMISSION_LOCATION_ALWAYS being set in XCode under 'Preprocessor Macros'. If not set (the default) then the permission request for 'always' is not included in the binary, therefore not triggering a complaint by Apple.

The request for 'always' (background) location updates is rare, so it is reasonable to have this turned off by default, and require some effort by the developer to activate this.
@781flyingdutchman
Copy link
Contributor Author

I've submitted a PR for this

mvanbeusekom pushed a commit that referenced this issue Feb 12, 2024
* Issue #1403 - On iOS, add conditional compile of the request for 'always' permission, to prevent users getting rejected by Apple for not including NSLocationAlwaysAndWhenInUseUsageDescription even if they never require background location updates.

The compile is conditional on the flag PERMISSION_LOCATION_ALWAYS being set in XCode under 'Preprocessor Macros'. If not set (the default) then the permission request for 'always' is not included in the binary, therefore not triggering a complaint by Apple.

The request for 'always' (background) location updates is rare, so it is reasonable to have this turned off by default, and require some effort by the developer to activate this.

* Changed PERMISSION_LOCATION_ALWAYS to BYPASS_PERMISSION_LOCATION_ALWAYS such that the default behavior (no flag set) remains the same, and the change is non-breaking.

Updated pubspec.yaml, README.md and CHANGELOG.md

* Refinements to permission bypass for background location.

Updates to CHANGELOG.md for geolocator and geolocator_apple
Added code to Podfile for the examples in geolocator and geolocator_apple that - if uncommented - will bypass the permission request by automatically setting the bypass flag upon compilation.
Updated README.md per suggestion
@mvanbeusekom
Copy link
Member

Solved in PR #1404, thanks @781flyingdutchman.

@yogeshelevn
Copy link

yogeshelevn commented Mar 20, 2024

@mvanbeusekom @781flyingdutchman The current library version does not address this issue. I encounter it consistently whenever attempting to release the app to the app store. So far, I've attempted to clean the Flutter project and have followed the two solutions outlined in the official library documentation, yet to no avail. This roadblock is hindering me from releasing the app on the Play Store. Your assistance in resolving this matter would be greatly appreciated. Please Help.
1 --> f you don't need to receive updates when your app is in the background, then add a compiler flag as follows: in XCode, click on Pods, choose the Target 'geolocator_apple', choose Build Settings, in the search box look for 'Preprocessor Macros' then add the BYPASS_PERMISSION_LOCATION_ALWAYS=1 flag. Setting this flag prevents your app from requiring the NSLocationAlwaysAndWhenInUseUsageDescription entry in Info.plist, and avoids questions from Apple when submitting your app.
2 -->

post_install do |installer|
  installer.pods_project.targets.each do |target|
    if target.name == "geolocator_apple"
      target.build_configurations.each do |config|
        config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= ['$(inherited)', 'BYPASS_PERMISSION_LOCATION_ALWAYS=1']
      end
    end
  end
end

ITMS-90683: Missing purpose string in Info.plist - Your app’s code references one or more APIs that access sensitive user data, or the app has one or more entitlements that permit such access. The Info.plist file for the “Runner.app” bundle should contain a NSLocationAlwaysAndWhenInUseUsageDescription key with a user-facing purpose string explaining clearly and completely why your app needs the data. 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. For details, visit: https://developer.apple.com/documentation/uikit/protecting_the_user_s_privacy/requesting_access_to_protected_resources.

@781flyingdutchman
Copy link
Contributor Author

That is strange: the use of compiler flags ensures that the code containing references to the API for background location is skipped (not compiled) so it does not appear in the binary that you submit to Apple. The fact that you get this response still from Apple suggests that the flag is not set correctly (by default, the code containing the API reference is compiled).

As I mentioned to @mvanbeusekom the structure of the plugin makes it difficult for me to test, but what I can say is that if the flag is set correctly, then the code that references the sensitive API should show 'dimmed' in Xcode. Perhaps you can check if that's the case for you @yogeshelevn : the file is https://github.com/Baseflow/flutter-geolocator/blob/main/geolocator_apple/ios/Classes/Handlers/PermissionHandler.m, so find that file in your XCode project, and the lines that should show dimmed are 76-78 and 95-104. If they don't show dimmed after you have set the flag manually, then somehow you have not set the flag correctly!

@yogeshelevn
Copy link

yogeshelevn commented Mar 21, 2024

@781flyingdutchman I checked in my xcode project. line number from 76-78, 95-104 is dimmed as you mentioned still I am getting the issue. This is my PodFile and please do let me lnow it is correct or not.

# Uncomment this line to define a global platform for your project
# platform :ios, '11.0'
platform :ios, '14.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'

project 'Runner', {
  'Debug' => :debug,
  'Profile' => :release,
  'Release' => :release
}

def flutter_root
  generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
  unless File.exist?(generated_xcode_build_settings_path)
    raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
  end

  File.foreach(generated_xcode_build_settings_path) do |line|
    matches = line.match(/FLUTTER_ROOT\=(.*)/)
    return matches[1].strip if matches
  end
  raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
end

require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)

flutter_ios_podfile_setup

target 'Runner' do
  use_frameworks!
  use_modular_headers!

  flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
  target 'RunnerTests' do
    inherit! :search_paths
  end
end

# post_install do |installer|
#   installer.pods_project.targets.each do |target|
#     if target.name == "geolocator_apple"
#       target.build_configurations.each do |config|
#         config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= ['$(inherited)', 'BYPASS_PERMISSION_LOCATION_ALWAYS=1']
#       end
#     end
#   end
# end

post_install do |installer|
  installer.pods_project.targets.each do |target|
    flutter_additional_ios_build_settings(target)
    if target.name == "geolocator_apple"
      target.build_configurations.each do |config|
        config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= ['$(inherited)', 'BYPASS_PERMISSION_LOCATION_ALWAYS=1']
      end
    end
    target.build_configurations.each do |config|
      config.build_settings['ONLY_ACTIVE_ARCH'] = 'NO'
      config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '14.0'
      # You can remove unused permissions here
      # for more infomation: https://github.com/BaseflowIT/flutter-permission-handler/blob/master/permission_handler/ios/Classes/PermissionHandlerEnums.h
      # e.g. when you don't need camera permission, just add 'PERMISSION_CAMERA=0'
      config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [
        '$(inherited)',
        'PERMISSION_CONTACTS=1',
        ## dart: PermissionGroup.camera
        'PERMISSION_CAMERA=1',
        ## dart: PermissionGroup.photos
        'PERMISSION_PHOTOS=1',
        ## dart: [PermissionGroup.location, PermissionGroup.locationAlways, PermissionGroup.locationWhenInUse]
        'PERMISSION_LOCATION=1',
        'PERMISSION_NOTIFICATIONS=1',
        ## dart: PermissionGroup.mediaLibrary
        'PERMISSION_MEDIA_LIBRARY=1',
      ]
    end
    awesome_pod_file = File.expand_path(File.join('plugins', 'awesome_notifications', 'ios', 'Scripts', 'AwesomePodFile'), '.symlinks')
    require awesome_pod_file
    update_awesome_pod_build_settings(installer)
  end
  awesome_pod_file = File.expand_path(File.join('plugins', 'awesome_notifications', 'ios', 'Scripts', 'AwesomePodFile'), '.symlinks')
  require awesome_pod_file
  update_awesome_main_target_settings('Runner', File.dirname(File.realpath(__FILE__)), flutter_root)
end

https://drive.google.com/file/d/1EHP7dOXJQFmXE9M-6UOIwftIsZH_9k6X/view?usp=sharing
https://drive.google.com/file/d/1g20urjIxHxNQwqFdEmgjfeiz2Q0RBQ_O/view?usp=sharing

@781flyingdutchman
Copy link
Contributor Author

Very strange. @mvanbeusekom do you have any idea how this could then still get compiled? Did you come across this issue with anyone else?

@mvanbeusekom
Copy link
Member

Hi @yogeshelevn,

I think the problem here is not with the configuration of the geolocator but with the permission_handler plugin. From the supplied content of the Podfile I see the application uses the permission_handler plugin and enables the PERMISSION_LOCATION setting.

Unfortunately the permission_handler does not yet make a distinction between "Location when in use" or "Location always" and declaring the PERMISSION_LOCATION=1 definition includes Objective-C code that calls the requestAlwaysAuthorization method of the CLLocationManager SDK. This is most likely what Apple's static code analysis causes to trigger the error.

At the moment there are two workarounds for you to take:

  1. Include the missing NSLocationAlwaysAndWhenInUseUsageDescription key in the ios/Runner/Info.plist or;
  2. Fallback on the geolocator plugin to request the location permissions and disable / remove the PERMISSION_LOCATION=1 line from the ios/Podfile.

Alternatively I will research if we can add additional macros in the permission_handler plugin to allow more fine grained control.

@mvanbeusekom
Copy link
Member

@yogeshelevn, we have just release version 11.3.1 of the permission_handler plugin which is updated to allow the use of only "When in use" permission.

To configure this replace the PERMISSION_LOCATION=1 macro in the ios/Podfile with the PERMISSION_LOCATION_WHENINUSE=1 macro. In your case the ios/Podfile will look like this:

# Uncomment this line to define a global platform for your project
# platform :ios, '11.0'
platform :ios, '14.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'

project 'Runner', {
  'Debug' => :debug,
  'Profile' => :release,
  'Release' => :release
}

def flutter_root
  generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
  unless File.exist?(generated_xcode_build_settings_path)
    raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
  end

  File.foreach(generated_xcode_build_settings_path) do |line|
    matches = line.match(/FLUTTER_ROOT\=(.*)/)
    return matches[1].strip if matches
  end
  raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
end

require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)

flutter_ios_podfile_setup

target 'Runner' do
  use_frameworks!
  use_modular_headers!

  flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
  target 'RunnerTests' do
    inherit! :search_paths
  end
end

# post_install do |installer|
#   installer.pods_project.targets.each do |target|
#     if target.name == "geolocator_apple"
#       target.build_configurations.each do |config|
#         config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= ['$(inherited)', 'BYPASS_PERMISSION_LOCATION_ALWAYS=1']
#       end
#     end
#   end
# end

post_install do |installer|
  installer.pods_project.targets.each do |target|
    flutter_additional_ios_build_settings(target)
    if target.name == "geolocator_apple"
      target.build_configurations.each do |config|
        config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= ['$(inherited)', 'BYPASS_PERMISSION_LOCATION_ALWAYS=1']
      end
    end
    target.build_configurations.each do |config|
      config.build_settings['ONLY_ACTIVE_ARCH'] = 'NO'
      config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '14.0'
      # You can remove unused permissions here
      # for more infomation: https://github.com/BaseflowIT/flutter-permission-handler/blob/master/permission_handler/ios/Classes/PermissionHandlerEnums.h
      # e.g. when you don't need camera permission, just add 'PERMISSION_CAMERA=0'
      config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [
        '$(inherited)',
        'PERMISSION_CONTACTS=1',
        ## dart: PermissionGroup.camera
        'PERMISSION_CAMERA=1',
        ## dart: PermissionGroup.photos
        'PERMISSION_PHOTOS=1',
        ## dart: [PermissionGroup.location, PermissionGroup.locationWhenInUse]
        'PERMISSION_LOCATION_WHENINUSE=1',
        'PERMISSION_NOTIFICATIONS=1',
        ## dart: PermissionGroup.mediaLibrary
        'PERMISSION_MEDIA_LIBRARY=1',
      ]
    end
    awesome_pod_file = File.expand_path(File.join('plugins', 'awesome_notifications', 'ios', 'Scripts', 'AwesomePodFile'), '.symlinks')
    require awesome_pod_file
    update_awesome_pod_build_settings(installer)
  end
  awesome_pod_file = File.expand_path(File.join('plugins', 'awesome_notifications', 'ios', 'Scripts', 'AwesomePodFile'), '.symlinks')
  require awesome_pod_file
  update_awesome_main_target_settings('Runner', File.dirname(File.realpath(__FILE__)), flutter_root)
end

Hope this helps, if you have any problems please create an issue in the repo of the permission_handler plugin.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
P2 Important issues not at the top of the work list. type: enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

4 participants