Flutter-permission-handler: Apple Connect reject my app with unuse permission

Created on 2 May 2020  路  21Comments  路  Source: Baseflow/flutter-permission-handler

馃悰 Bug Report

ITMS-90683: Missing Purpose String in Info.plist - Your app's code references one or more APIs that access sensitive user data. The app's Info.plist file should contain a NSLocationAlwaysUsageDescription key with a user-facing purpose string explaining clearly and completely why your app needs the data. Starting Spring 2019, all apps submitted to the App Store that access user data are 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. Learn more (https://developer.apple.com/documentation/uikit/core_app/protecting_the_user_s_privacy).

ITMS-90683: Missing Purpose String in Info.plist - Your app's code references one or more APIs that access sensitive user data. The app's Info.plist file should contain a NSLocationWhenInUseUsageDescription key with a user-facing purpose string explaining clearly and completely why your app needs the data. Starting Spring 2019, all apps submitted to the App Store that access user data are 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. Learn more (https://developer.apple.com/documentation/uikit/core_app/protecting_the_user_s_privacy).

podfile

# Uncomment this line to define a global platform for your project
# platform :ios, '9.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 parse_KV_file(file, separator='=')
  file_abs_path = File.expand_path(file)
  if !File.exists? file_abs_path
    return [];
  end
  generated_key_values = {}
  skip_line_start_symbols = ["#", "/"]
  File.foreach(file_abs_path) do |line|
    next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ }
    plugin = line.split(pattern=separator)
    if plugin.length == 2
      podname = plugin[0].strip()
      path = plugin[1].strip()
      podpath = File.expand_path("#{path}", file_abs_path)
      generated_key_values[podname] = podpath
    else
      puts "Invalid plugin specification: #{line}"
    end
  end
  generated_key_values
end

target 'Runner' do
  use_frameworks!
  use_modular_headers!

  # Flutter Pod

  copied_flutter_dir = File.join(__dir__, 'Flutter')
  copied_framework_path = File.join(copied_flutter_dir, 'Flutter.framework')
  copied_podspec_path = File.join(copied_flutter_dir, 'Flutter.podspec')
  unless File.exist?(copied_framework_path) && File.exist?(copied_podspec_path)
    # Copy Flutter.framework and Flutter.podspec to Flutter/ to have something to link against if the xcode backend script has not run yet.
    # That script will copy the correct debug/profile/release version of the framework based on the currently selected Xcode configuration.
    # CocoaPods will not embed the framework on pod install (before any build phases can generate) if the dylib does not exist.

    generated_xcode_build_settings_path = File.join(copied_flutter_dir, 'Generated.xcconfig')
    unless File.exist?(generated_xcode_build_settings_path)
      raise "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter pub get is executed first"
    end
    generated_xcode_build_settings = parse_KV_file(generated_xcode_build_settings_path)
    cached_framework_dir = generated_xcode_build_settings['FLUTTER_FRAMEWORK_DIR'];

    unless File.exist?(copied_framework_path)
      FileUtils.cp_r(File.join(cached_framework_dir, 'Flutter.framework'), copied_flutter_dir)
    end
    unless File.exist?(copied_podspec_path)
      FileUtils.cp(File.join(cached_framework_dir, 'Flutter.podspec'), copied_flutter_dir)
    end
  end

  # Keep pod path relative so it can be checked into Podfile.lock.
  pod 'Flutter', :path => 'Flutter'

  # Plugin Pods

  # Prepcdare symlinks folder. We use symlinks to avoid having Podfile.lock
  # referring to absolute paths on developers' machines.
  system('rm -rf .symlinks')
  system('mkdir -p .symlinks/plugins')
  plugin_pods = parse_KV_file('../.flutter-plugins')
  plugin_pods.each do |name, path|
    symlink = File.join('.symlinks', 'plugins', name)
    File.symlink(path, symlink)
    pod name, :path => File.join(symlink, 'ios')
  end
end

# Prevent Cocoapods from embedding a second Flutter framework and causing an error with the new Xcode build system.
install! 'cocoapods', :disable_input_output_paths => true

# post_install do |installer|
#   installer.pods_project.targets.each do |target|
#     target.build_configurations.each do |config|
#       config.build_settings['ENABLE_BITCODE'] = 'NO'
#     end
#   end
# end

# PermissionHandler鍒犻櫎鏃犵敤鏉冮檺
post_install do |installer|
  installer.pods_project.targets.each do |target|
    target.build_configurations.each do |config|
      config.build_settings['ENABLE_BITCODE'] = 'NO'
        config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [
          '$(inherited)',

        ## dart: PermissionGroup.calendar
          'PERMISSION_EVENTS=0',

        ## dart: PermissionGroup.reminders
          'PERMISSION_REMINDERS=0',

        ## dart: PermissionGroup.contacts
          'PERMISSION_CONTACTS=0',

        ## dart: PermissionGroup.camera
        #  'PERMISSION_CAMERA=0',

        ## dart: PermissionGroup.microphone
          'PERMISSION_MICROPHONE=0',

        ## dart: PermissionGroup.speech
          'PERMISSION_SPEECH_RECOGNIZER=0',

        ## dart: PermissionGroup.photos
          'PERMISSION_PHOTOS=0',

        ## dart: [PermissionGroup.location, PermissionGroup.locationAlways, PermissionGroup.locationWhenInUse]
          'PERMISSION_LOCATION=0',

        ## dart: PermissionGroup.notification
          'PERMISSION_NOTIFICATIONS=0',

        ## dart: PermissionGroup.mediaLibrary
          'PERMISSION_MEDIA_LIBRARY=0',

        ## dart: PermissionGroup.sensors
          'PERMISSION_SENSORS=0'
      ]
    end
  end
end

pubspec.yaml

environment:
  sdk: ">=2.4.0 <3.0.0"

dependencies:
  flutter:
    sdk: flutter

  flutter_localizations:
    sdk: flutter

  cupertino_icons: ^0.1.2

  dio: ^3.0.8
  dio_cookie_manager: ^1.0.0
  cookie_jar: ^1.0.0

  provider: ^3.2.0

  webview_flutter: ^0.3.19+2

  quiver: ^2.0.5

  open_file: ^2.1.1

  device_info: ^0.4.0+1

  package_info: ^0.4.0+6

  connectivity: ^0.4.6+2

  shared_preferences: ^0.5.6

  localstorage: ^3.0.1+4

  permission_handler: ^5.0.0+hotfix.4

  url_launcher: ^5.4.1

  decimal: ^0.3.5

  bot_toast: ^2.4.1

  flutter_spinkit: "^4.1.2"

  shimmer: ^1.0.1

  qrcode: ^1.0.4

  pull_to_refresh: ^1.5.8

  cached_network_image: ^2.0.0

  flutter_screenutil: ^0.5.0

  badges: ^1.1.1

  mp_chart: ^0.1.9

info.plist

only have

NSCameraUsageDescription

Version: ^5.0.0+hotfix.4

Platform:

  • [v] :iphone: iOS
  • [ ] :robot: Android

Most helpful comment

From my experience I can tell that if you provide the permissions string similar to "We are not using location permission", "The camera permission is not required" or "The app doesn't require access to sensors" then the review is positive. As long as you don't ask for these permissions then it should be ok.

It's fairly common that some 3rd party libraries use references in code to some API requiring permission but aren't actually used (e.g. location). The Apple analysis will detect it in symbols and will require adding a corresponding permission string. If you are not using it then you shouldn't worry too much.

From a purist point of view, however, I would like to be able to remove these permissions if that's stated in the documentation of the plugin.

All 21 comments

I have the same issue with few other permissions

If you do not need Location permissions of all type you can add the following in your Podfile 'PERMISSION_LOCATION=0'
In my case i need only location while in use , so i cannot add this line of code.

Even though I have added this line in my podfile as stated in the readme I received warning from AppStore Connect that I need to provide NSLocationWhenInUseUsageDescription

I have the same problem.

does some one fixed the issue?

I'm experimenting with some approaches (adding to build settings of target, project, schemes etc.) but nothing successful for now.

The only thing that worked was just adding the necessary permission strings to Info.plist. I added dummy texts such as "The app doesn't require access to location" etc.

after added the dummy texts, does show any dummy text to end user?

No, the text will be shown only when you actually request the permission from the user

Thank you, the great action.

No, the text will be shown only when you actually request the permission from the user

@orestesgaolin Thanks for your advice, but i guess Apple requires an explanation in App description or as a review note , why do we actually included this type of permission in out App. Did that happened to you?

From my experience I can tell that if you provide the permissions string similar to "We are not using location permission", "The camera permission is not required" or "The app doesn't require access to sensors" then the review is positive. As long as you don't ask for these permissions then it should be ok.

It's fairly common that some 3rd party libraries use references in code to some API requiring permission but aren't actually used (e.g. location). The Apple analysis will detect it in symbols and will require adding a corresponding permission string. If you are not using it then you shouldn't worry too much.

From a purist point of view, however, I would like to be able to remove these permissions if that's stated in the documentation of the plugin.

@aliakkawi the permission_handler lib added the permissions, so we can't remove the permissions.

I have commit to the apple connect review . wait the result.

@terry8204 Wish you good luck, update us with the results please.

Opinion:

For cases where your app does not need a given permission and you have to have a description string, I suggest instructing the user to deny the permission. "This app does not require access to ...X. Please deny this request." (Bonus points for instructing the user to alert you.)

Why? In case we get a rogue flutter package/library with malware - you can at least try to limit what that code can do.

@aliakkawi good luck, has passed the apple review.

@terry8204 Congratulation!, glad to hear that, thanks for updating us!.

add dummy texts such as "The app doesn't require access to location" ? Can the plugin fit to use permission when what we use?

Although add the unuse text can pass the apple review, it doesn't what we want, what do you think?

I guess the connectivity plugin requires NSLocationAlwaysUsageDescription/NSLocationWhenInUseUsageDescription permission, not permission-handler.

I wouldn't suggest using the approach of adding the permissions to your Info.plist if you're not using them. When users go to install your application on iOS 14 they'll see in the privacy details that your app is requesting all these permissions even though it isn't using them. It will lead users to pass on installing your app.

Was this page helpful?
0 / 5 - 0 ratings