使用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来解决问题了

相关推荐
南望无一1 天前
React Native 0.70.x如何从本地安卓源码(ReactAndroid)构建
前端·react native
开发者每周简报3 天前
React:UI开发的革新者
javascript·react native·react.js
朦胧之10 天前
React Native 新架构 (一)
前端·react native
野槐12 天前
react实例与总结(一)
javascript·react native·react.js
GISer_Jing13 天前
React Native:跨平台移动应用开发的明星框架
javascript·react native·react.js
No Silver Bullet15 天前
ReactNative进阶(五十九):存量 react-native 项目适配 HarmonyOS NEXT
react native·react.js·harmonyos
江湖行骗老中医15 天前
React Native 开发 安卓项目构建工具Gradle的配置和使用
android·react native·react.js
@大迁世界15 天前
React Native 列表组件:FlashList、FlatList 及更多
javascript·react native·react.js·ecmascript
screct_demo21 天前
详细介绍 React Native 的动画系统。主要包括 Animated 组件的各种用法:
javascript·react native·react.js
朦胧之22 天前
Expo 框架开发移动应用
前端·react native