使用Taro开发iOS App触发额外权限请求的问题

问题描述

基于Taro开发原生应用,在iOS端会在业务代码完全没有使用相关API的情况下,提示用户授权运动与健身权限

版本信息

ini 复制代码
Taro CLI 3.6.24 environment info:
    System:
      OS: macOS 15.1.1
      Shell: 5.9 - /bin/zsh
    Binaries:
      Node: 20.3.1 - ~/.nvm/versions/node/v20.3.1/bin/node
      Yarn: 1.22.19 - ~/.nvm/versions/node/v20.3.1/bin/yarn
      npm: 9.6.7 - ~/.nvm/versions/node/v20.3.1/bin/npm
    npmPackages:
      @tarojs/cli: 3.6.1 => 3.6.1 
      @tarojs/components: 3.6.1 => 3.6.1 
      @tarojs/helper: 3.6.1 => 3.6.1 
      @tarojs/plugin-framework-react: 3.6.1 => 3.6.1 
      @tarojs/react: 3.6.1 => 3.6.1 
      @tarojs/rn-runner: 3.6.1 => 3.6.1 
      @tarojs/router: 3.6.1 => 3.6.1 
      @tarojs/runtime: 3.6.1 => 3.6.1 
      @tarojs/shared: 3.6.1 => 3.6.1 
      @tarojs/taro: 3.6.1 => 3.6.1 
      @tarojs/taro-rn: 3.6.1 => 3.6.1 
      @tarojs/webpack5-runner: 3.6.1 => 3.6.1 
      babel-preset-taro: 3.6.1 => 3.6.1 
      expo: ~47.0.3 => 47.0.13 
      react: 18.1.0 => 18.1.0 
      react-native: 0.70.7 => 0.70.7 

复现方法

该问题是在线上应用通过code-push热更新之后出现的,在触发热更新之前,整个应用在使用期间都不会请求该权限(因为毕竟没有使用到嘛),但是当热更新成功(即重新挂载jsbundle)之后,用户就会看到运动与健身权限的使用申请。

开发环境的话,可以通过npm run start启动metro服务,然后在应用内ReloadJS触发

问题分析

一开始我认为是其他同事在开发别的业务的时候使用到了相关API,但是通过全局搜索,发现并没有。

直到我精简了所有代码,让整个js业务代码部分只剩一个hello world以后,问题依然存在,我才确认,问题并不存在于js层,而应该是原生层的问题。

通过查阅苹果官方对于NSMotionUsageDescription这个权限的描述,该权限仅在应用使用了CMSensorRecorder, CMPedometer, CMMotionActivityManager, 和 CMMovementDisorderManager这4个底层API时才会触发,通过全局搜索,发现只有expo-sensors这个依赖使用到了CMPedometer

通过查询taro-native-shell的GitHub仓库,在readme中有列出各个expo包对应的Taro API如下:

css 复制代码
{
  authorize: Set(3) {
    'expo-camera@~14.0.0',
    'expo-image-picker@~14.7.0',
    'expo-location@~16.5.1'
  },
  chooseImage: Set(4) {
    '@react-native-camera-roll/camera-roll@~7.2.0',
    'expo-camera@~14.0.0',
    'expo-image-picker@~14.7.0',
    'react-native-safe-area-context@~4.8.0'
  },
  chooseMedia: Set(4) {
    '@react-native-camera-roll/camera-roll@~7.2.0',
    'expo-camera@~14.0.0',
    'expo-image-picker@~14.7.0',
    'react-native-safe-area-context@~4.8.0'
  },
  chooseVideo: Set(4) {
    '@react-native-camera-roll/camera-roll@~7.2.0',
    'expo-camera@~14.0.0',
    'expo-image-picker@~14.7.0',
    'react-native-safe-area-context@~4.8.0'
  },
  clearStorage: Set(1) { '@react-native-async-storage/async-storage@~1.21.0' },
  compressImage: Set(1) { '@bam.tech/react-native-image-resizer@~3.0.5' },
  createCameraContext: Set(1) { 'expo-camera@~14.0.0' },
  createInnerAudioContext: Set(1) { 'expo-av@~13.10.0' },
  downloadFile: Set(1) { 'expo-file-system@~16.0.0' },
  getAppBaseInfo: Set(1) { 'react-native-device-info@~10.12.0' },
  getClipboardData: Set(1) { '@react-native-clipboard/clipboard@~1.13.0' },
  getFileInfo: Set(1) { 'expo-file-system@~16.0.0' },
  getLocation: Set(2) {
    '@react-native-community/geolocation@~3.1.0',
    'expo-location@~16.5.1'
  },
  getNetworkType: Set(1) { '@react-native-community/netinfo@~11.2.0' },
  getRecorderManager: Set(2) { 'expo-av@~13.10.0', 'expo-file-system@~16.0.0' },
  getSavedFileInfo: Set(1) { 'expo-file-system@~16.0.0' },
  getSavedFileList: Set(1) { 'expo-file-system@~16.0.0' },
  getScreenBrightness: Set(1) { 'expo-brightness@~11.8.0' },
  getSetting: Set(3) {
    'expo-camera@~14.0.0',
    'expo-image-picker@~14.7.0',
    'expo-location@~16.5.1'
  },
  getStorage: Set(1) { '@react-native-async-storage/async-storage@~1.21.0' },
  getStorageInfo: Set(1) { '@react-native-async-storage/async-storage@~1.21.0' },
  getSystemInfo: Set(2) {
    'react-native-safe-area-context@~4.8.0',
    'react-native-device-info@~10.12.0'
  },
  getSystemInfoSync: Set(2) {
    'react-native-safe-area-context@~4.8.0',
    'react-native-device-info@~10.12.0'
  },
  offAccelerometerChange: Set(1) { 'expo-sensors@~12.9.0' },
  offDeviceMotionChange: Set(1) { 'expo-sensors@~12.9.0' },
  offGyroscopeChange: Set(1) { 'expo-sensors@~12.9.0' },
  offLocationChange: Set(1) { '@react-native-community/geolocation@~3.1.0' },
  offNetworkStatusChange: Set(1) { '@react-native-community/netinfo@~11.2.0' },
  onAccelerometerChange: Set(1) { 'expo-sensors@~12.9.0' },
  onDeviceMotionChange: Set(1) { 'expo-sensors@~12.9.0' },
  onGyroscopeChange: Set(1) { 'expo-sensors@~12.9.0' },
  onLocationChange: Set(1) { '@react-native-community/geolocation@~3.1.0' },
  onNetworkStatusChange: Set(1) { '@react-native-community/netinfo@~11.2.0' },
  openSetting: Set(3) {
    'expo-camera@~14.0.0',
    'expo-image-picker@~14.7.0',
    'expo-location@~16.5.1'
  },
  previewImage: Set(5) {
    '@react-native-camera-roll/camera-roll@~7.2.0',
    'expo-camera@~14.0.0',
    'expo-image-picker@~14.7.0',
    'react-native-safe-area-context@~4.8.0',
    'expo-file-system@~16.0.0'
  },
  removeSavedFile: Set(1) { 'expo-file-system@~16.0.0' },
  removeStorage: Set(1) { '@react-native-async-storage/async-storage@~1.21.0' },
  saveFile: Set(1) { 'expo-file-system@~16.0.0' },
  saveImageToPhotosAlbum: Set(4) {
    '@react-native-camera-roll/camera-roll@~7.2.0',
    'expo-camera@~14.0.0',
    'expo-image-picker@~14.7.0',
    'react-native-safe-area-context@~4.8.0'
  },
  saveVideoToPhotosAlbum: Set(4) {
    '@react-native-camera-roll/camera-roll@~7.2.0',
    'expo-camera@~14.0.0',
    'expo-image-picker@~14.7.0',
    'react-native-safe-area-context@~4.8.0'
  },
  scanCode: Set(5) {
    'react-native-safe-area-context@~4.8.0',
    'expo-camera@~14.0.0',
    'expo-barcode-scanner@~12.9.0',
    '@react-native-camera-roll/camera-roll@~7.2.0',
    'expo-image-picker@~14.7.0'
  },
  setClipboardData: Set(1) { '@react-native-clipboard/clipboard@~1.13.0' },
  setKeepScreenOn: Set(1) { 'expo-keep-awake@~12.8.0' },
  setScreenBrightness: Set(1) { 'expo-brightness@~11.8.0' },
  setStorage: Set(1) { '@react-native-async-storage/async-storage@~1.21.0' },
  showActionSheet: Set(1) { 'react-native-safe-area-context@~4.8.0' },
  startAccelerometer: Set(1) { 'expo-sensors@~12.9.0' },
  startDeviceMotionListening: Set(1) { 'expo-sensors@~12.9.0' },
  startGyroscope: Set(1) { 'expo-sensors@~12.9.0' },
  startLocationUpdate: Set(1) { '@react-native-community/geolocation@~3.1.0' },
  stopAccelerometer: Set(1) { 'expo-sensors@~12.9.0' },
  stopDeviceMotionListening: Set(1) { 'expo-sensors@~12.9.0' },
  stopGyroscope: Set(1) { 'expo-sensors@~12.9.0' },
  stopLocationUpdate: Set(1) { '@react-native-community/geolocation@~3.1.0' },
  uploadFile: Set(1) { 'expo-file-system@~16.0.0' }
}

通过以上列表,可以知道offAccelerometerChangeoffDeviceMotionChangeoffGyroscopeChangeonAccelerometerChangeonDeviceMotionChangeonGyroscopeChange这6个API都是依赖expo-sensors的,而全局检索后,我进一步确认,这6个API在我们的业务代码中都没有使用到,那么就可以考虑通过移除expo-sensors的方式尝试修复这个问题

修复方案

  1. 查看项目自身的package.json,移除所有expo-sensors相关的依赖项声明
  2. 移除node_modules/@tarojs/taro-rn/package.json中关于expo-sensors的依赖声明
  3. 移除node_modules/@tarojs/taro-rn/libList.jsoffAccelerometerChangeoffDeviceMotionChangeoffGyroscopeChangeonAccelerometerChangeonDeviceMotionChangeonGyroscopeChange这6个API的导出
  4. 使用patch-package将修改同步到我们自己的代码仓库里
java 复制代码
npx patch-package @tarojs/taro-rn --exclude 'nothing'

小tips,直接使用npx patch-package @tarojs/taro-rn默认情况下会忽略package.json文件内的改动,需要加上参数--exclude 'nothing'确保把package.json的改动也记录到patch中

  1. 更新iOS中相关的依赖模块,应该能看到Removing EXSensors字样
bash 复制代码
cd ios
pod install
  1. 重新运行react-native run-ios或者在Xcode中点击Run按钮重新构建应用程序,根据之前的复现方法验证,不再触发该权限

ToDo

虽然问题暂时是解决了,但是为什么在js代码被重新加载时会触发隐私权限请求依然没有研究明白,所以这个解决方案是有瑕疵的。

如果是"有使用运动与健身相关API,但是因为热更新的原因,在非使用场景触发了隐私权限请求"这样的场景,就不能简单通过移除expo-sensors来解决问题了

相关推荐
凌鲨3 小时前
React Native学习路线图
学习·react native·react.js
一个处女座的程序猿O(∩_∩)O7 小时前
四大跨平台开发框架深度解析——uniapp、uniapp-X、React Native与Flutter
flutter·react native·uni-app
Domain-zhuo2 天前
如何理解React State不可变性的原则
前端·javascript·react native·react.js·前端框架·ecmascript
wuwuFQ4 天前
React Native状态管理器Redux、MobX、Context API、useState
javascript·react native·react.js
爱lv行5 天前
创建 React Native 项目
javascript·react native·react.js
某柚啊5 天前
Hermes engine on React Native 0.72.5,function无法toString转成字符串
javascript·react native·react.js
Domain-zhuo6 天前
React的功能是什么?
前端·javascript·react native·react.js·前端框架·ecmascript
wayne2147 天前
ReactNative接入广告平台三方库推荐
javascript·react native·react.js
sir.山11 天前
taro小程序进入腾讯验证码
小程序·taro·验证码·腾讯验证码·uniapp验证码·原生验证码