更新记录

2.0.0(2026-11-06)

封装了IOS原生webview,重复弹出麦克风和相机权限问题


平台兼容性

uni-app(4.0)

Vue2 Vue3 Chrome Safari app-vue app-nvue Android iOS 鸿蒙
√ √ - - - - - √ -
微信小程序 支付宝小程序 抖音小程序 百度小程序 快手小程序 京东小程序 鸿蒙元服务 QQ小程序 飞书小程序 小红书小程序 快应用-华为 快应用-联盟
- - - - - - - - - - - -

uni-app x(4.0)

Chrome Safari Android iOS 鸿蒙 微信小程序
- - - √ - -

其他

多语言 暗黑模式 宽屏模式 蒸汽模式
× × × √

hy-media-auto-grant

iOS WKWebView 网页层摄像头/麦克风(getUserMedia)自动授权插件。

1. 介绍

解决什么问题

uni-app 的 <web-view> 加载的 H5 页面在 iOS 上调用 getUserMedia(如 TRTC Web SDK 视频问诊)时,WKWebView 会每次弹出网页层授权弹窗——即使 App 系统权限已经授权。原因:

  • App 系统权限(TCC)与 WKWebView 网页层权限是两层独立体系;
  • WKWebView 不持久化网页授权,且 uni-app 的 web-view 子 webview 没有设置 uiDelegate,WebKit 因此走默认弹窗路径。

本插件扫描窗口树中的 WKWebView,将其 uiDelegate 替换为自带的媒体授权代理。此后:

  • 系统层摄像头 + 麦克风均已授权 → 网页层授权自动 grant,不再弹窗;
  • 任一系统权限未授权 → decisionHandler(.prompt),弹出与原生默认一致的授权窗(不绕过、不伪造系统授权)。

影响范围(重要)

维度 范围
平台 仅 iOS App(iOS 15+)。Android 为空实现(no-op);H5、小程序端不涉及
Webview 仅接管 URL 以 urlPrefix 开头的 H5 子 webview;uni-app 自身页面 webview(file://...__uniappview.html)及其他域名 webview 不受影响
权限类型 仅网页层摄像头/麦克风授权(requestMediaCapturePermissionForOrigin 回调)。地理定位、通知等其他 H5 授权不受影响
决策逻辑 双系统权限 authorized → 自动 grant;否则 .prompt(等同默认弹窗)
附带行为 alert() 不再弹、confirm() 返回 false、prompt() 返回 null(与之前 nil-delegate 默认行为一致)

前提条件

壳工程进入 webview 前申请 App 级系统权限的逻辑必须保留——本插件的 auto-grant 以系统权限已授权为前提。

2. 安装方法

方式一:本地 uni_modules(当前项目已内置)

插件位于 uni_modules/hy-media-auto-grant/,HBuilderX 导入工程后即可使用,无需额外安装。

方式二:复制到其他工程

将整个 uni_modules/hy-media-auto-grant/ 目录复制到目标工程的 uni_modules/ 下。

关键要求

  • iOS 最低版本 15.0(requestMediaCapturePermissionForOrigin 为 iOS 15+ API),已在插件 package.json 声明 app-ios.minVersion: 15.0;
  • manifest.json 中 iOS webview 内核必须是 WKWebview("app-plus" → "distributed" → "ios" → "WKWebview": true 或 UIWebview 关闭);
  • 修改插件内任何 UTS/Swift 源码后,必须重新打自定义基座(同时在插件 package.json 中递增 version 强制云编译);纯 JS 调用侧改动热推即可。

3. 代码演示

API 签名

// uni_modules/hy-media-auto-grant
export function installHook(
  cameraAuthorized: boolean,   // 系统层摄像头是否已授权
  microphoneAuthorized: boolean, // 系统层麦克风是否已授权
  urlPrefix: string            // 只接管 URL 以此开头的 webview,传空串时不接管任何 webview
): string
// 返回 JSON 字符串,需 JSON.parse:
// { "installed": boolean, "webviewCount": number, "totalWebviews": number, "urls": string[] }

完整调用示例(web-view 宿主页面)

<template>
  <view>
    <web-view :src="baseUrl" @message="onMessage"></web-view>
  </view>
</template>

<script>
// #ifdef APP-PLUS
import { installHook } from '@/uni_modules/hy-media-auto-grant';
// #endif

export default {
  data() {
    return {
      baseUrl: 'https://your-h5-domain.com'
    };
  },
  onReady() {
    // #ifdef APP-PLUS
    this.installMediaAutoGrant(0);
    // #endif
  },
  methods: {
    installMediaAutoGrant(retry) {
      // #ifdef APP-PLUS
      try {
        // 1. 查询 App 系统权限状态
        const setting = uni.getAppAuthorizeSetting();
        const cameraOk = setting.cameraAuthorized === 'authorized';
        const micOk = setting.microphoneAuthorized === 'authorized';

        // 2. 提取 baseUrl 的协议+域名+端口作为前缀(域名后可能直接跟 ?query,注意排除)
        const m = /^https?:\/\/[^\/?]+/.exec(this.baseUrl || '');
        const urlPrefix = m ? m[0] : '';

        // 3. 安装代理(幂等,已接管的 webview 自动跳过)
        const res = JSON.parse(installHook(cameraOk, micOk, urlPrefix));
        console.log('install:', JSON.stringify(res));
        // res 示例: { "installed": true, "webviewCount": 1, "totalWebviews": 1, "urls": ["https://your-h5-domain.com/xxx"] }

        // 4. web-view 子 webview 可能晚于 onReady 创建,定时重试兜底
        if (retry < 9) {
          setTimeout(() => this.installMediaAutoGrant(retry + 1), 1000);
        }
      } catch (e) {
        console.error('install error', e);
      }
      // #endif
    },
    onMessage(e) {
      const data = e.detail.data[0];
      // H5 跳转视频页前通知 App 端,立即再 install 一次(单次,不重试)
      if (data.type === 'watchRouterH5Change') {
        // #ifdef APP-PLUS
        this.installMediaAutoGrant(9);
        // #endif
      }
    }
  }
};
</script>

H5 侧配合(可选但推荐)

H5 在跳转需要摄像头/麦克风的页面前,通过 uni.postMessage 通知宿主:

// kernel-app H5 内
uni.webView.postMessage({
  data: { type: 'watchRouterH5Change' }
});

返回值说明

字段 类型 说明
installed boolean 是否执行了安装(urlPrefix 为空串时返回 false)
webviewCount number 本次新接管(安装代理)的 webview 数量
totalWebviews number 扫描到的全部 WKWebView 数量
urls string[] 扫描到的各 webview 当前 URL,用于排查

排查要点

  • webviewCount: 0 且 totalWebviews > 0:子 webview 的 URL 不匹配 urlPrefix,检查 baseUrl 配置与正则提取结果;
  • totalWebviews: 0:web-view 子 webview 尚未创建,靠重试链覆盖;
  • 接管后 H5 的 alert() 不再弹出,需提示的页面建议改用 Toast。

隐私、权限声明

1. 本插件需要申请的系统权限列表:

无

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

无

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

无

暂无用户评论。