解
Permissions APIに限らない話ではありますが、Web APIのルートであるNavigatorのプロパティに、使おうとするAPIが存在しているかをチェックする習慣をつけましょう、が結論です。
if ("permissions" in navigator) { // パーミッション使える } else { // 使えない }
みなさんはWeb APIを使う前に、まず上記のMDN web docsのNavigator一覧を読み、実験的機能であることを示す水色のビーカーアイコンがついていないことを確認しましょう。
〜 終 〜
以下メモです。
SafariでPermissions APIが動かなくて気づいた
先日作ったオンラインチャットができるWebアプリですが、初回アクセス時のカメラとマイクの使用前に「Permissions API」を使っています。
これを使うと、カメラやマイクのアクセス許可のポップアップが出る前にこちらから独自ポップアップを出して「悪さをするわけじゃないから大丈夫ですよ、許可ボタン押してくださいネ」と前もってユーザーに伝えておくことができます。
作っているときはChromiumベースのブラウザでテストしており、何も問題はなかったんですが、世の中って思った以上にiOSユーザー多いんですよね。困る。そういえばWebBluetoothとかも使えないし一体なんやねん
初期実装
Vue.jsベースです。
ページ状態がmountedのとき、カメラとマイクそれぞれに対してパーミッションが「許可」となっているか確認しています。
だいたいの場合で初回アクセス以後は allowed
となるので、引っかかることはありません。
mounted: async function() { // カメラとマイクのパーミッションを確認 if (await this.cam_allowed() && await this.mic_allowed()) { // 許可済みの場合はそのままgetUserMedia await this.startLocalDevice(); } else { // 未許可または拒否の場合はとりあえずダイアログを出す // このダイアログ内のボタンを明示的に押してonChangeLocalDeviceを発火させる this.permission_dialog_open = true; } }, methods: { // カメラ・マイクそれぞれのPermissionがgrantedかどうか cam_allowed: async function() { const cam = await navigator.permissions.query({name: 'camera'}); // granted/prompt/denied return (cam.state == 'granted'); }, mic_allowed: async function() { const mic = await navigator.permissions.query({name: 'microphone'}); // granted/prompt/denied return (mic.state == 'granted'); }, },
これをiOSのブラウザや、macOSでもSafariで開いたりなどすると、 navigator.permissions
が undefined
なので、途中で止まってしまったり謎挙動になったりしてしまいます。
プロパティチェックをいれる
上記例ではGeolocation APIですが、navigator直下に対象のAPIを示すプロパティが定義されているか調べればよいです。
Permissions APIで、今回の場合だと、以下の1行を各メソッドの一番上に入れればOKでした。
if (!("permissions" in navigator)) return true;
ここのコードでは、未定義であれば(パーミッション非対応ブラウザであれば)独自ポップアップが出ずに、直接「ブラウザにカメラ使用を許可しますか?」のダイアログが表示されるようになります。
UXとしては若干落ちるかもしれませんが、動くに越したことはないですからね。
一方で、組み込みJavascriptにおいてカメラやマイクを使うとき、ユーザーのクリックなどによる操作をもとに getUserMedia
を発火させねばならない、という場合があったような気がします。
そういったときは、常時ユーザーイベントのフックに基づいて動作するような実装にしないといけないかもしれません。
というか現時点で最新のiOS13のWKWebViewでもgetUserMediaは実装されてないらしいのでそもそも無駄足が過ぎる気がする