WebView と getUserMedia との奮闘記
この記事はAizu Advent Calendar 2020の 5 日目に差し込む記事です。
今年は某 SDK を利用して WebRTC を利用しており、WebRTC を利用するにあたって カメラ・音声取得処理と WebView がかなりの障害になったのでそれに関してまとめておこうかなと思います。
TL;DR
- User Agent だけでは WebView かどうか確実にはわからない
- audio, video の取得処理が失敗した際のエラーの種類を見て対象外のブラウザであることを通知しましょう (つまるところエラーハンドリングちゃんとやろうねってこと)
カメラ・音声取得
どういう経緯で必要だったかは書きませんが、ビデオ通話ができる必要があり、そのためにもカメラと音声を取得することが必須でした。 以下、簡単な例ですが取得するために必要なコードです。
try { const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true, }); } catch (error) { console.error("getUserMedia error:", error); }
これで MediaStream
オブジェクトがstream
内に入りこれをやりとりしてビデオ通話を実現します。
WebView の何が問題なのか
こちらを見ていただければわかりますが、主要なブラウザでは getUserMedia の利用は可能です。 なので WebView に関して何を考えるでもなく開発を進めていました。 しかし、アプリケーションを運用し始めると映像が映らないという報告が飛んでくるわけです。 幸いにも Sentry でエラーログを取っていたためそちらを見てみると概ね以下のパターンに分けられました。
AbortError
を吐いていて利用できないNotAllowedError
を吐いていて利用できないAbortError
/NotAllowedError
で UserAgent に wv が含まれるAbortError
/NotAllowedError
で UserAgent に YJApp-xxxx や FBXX、line など特定の文字列が含まれる
上記のエラーを見て、後半 2 つに関して着目し、WebView であることが起因してエラーを吐き利用ができないことに気が付きました。
UserAgent 内に特定の文字列が含まれる場合アラートを出す。
一番初めにとったアプローチがこれでした。(ちゃんとエラーハンドリングしろとのツッコミはなしでお願いします。)
エラーログを見る限り利用できないパターンのうちかなりの数は Android であればアプリ内ブラウザ、iOS であればアプリ内ブラウザに加え、Chrome などの他ブラウザだったため、一見するとメジャーなアプリ内ブラウザを提供しているアプリの UA を分析して特定の文字列を弾けば良さそうに思えました。
しかし、アプローチにはいくつか問題があります。
- WebView ではあるもののそれを示す文字列が含まれない
- Twitter など特定の文字列を UA に入れていないアプリ内ブラウザを提供するアプリがあること(分類としては上記と同じ)
- そもそもメジャーどころを判定してもユーザーが何を使ってアクセスしてくるかはわからないこと
以上の 3 つに大別されました。
もちろん WebView であってもアプリ側で適切に権限が付与する実装になっていれば getUserMedia
で映像・音声を取得できると思います。が、そういったケースは少なかったです。
例えばアプリの名前がブラウザとなっていても中身自体は WebView といったこともあり、しかも UA が通常のブラウザと変わらないといったこともあるのでこのアプローチは限界があるわけです。
エラーハンドリング
パターンわけを見て気づいたとは思いますが、利用できない場合必ずエラーを吐いています。つまりエラーを吐いた場合にユーザー側に利用できないことを通知しその後のフローに進ませないような実装になっていれば良いわけです。 そして利用していただく場合には Android なら Chrome, Firefox, Edge として iOS であれば Safari とのアナウンスを出せば、ブラウザを変えてくれるユーザーも出てくはずです。
まとめ
- User Agent だけでは WebView かどうか確実にはわからない
- audio, video の取得処理が失敗した際のエラーの種類を見て対象外のブラウザであることを通知しましょう (つまるところエラーハンドリングちゃんとやろうねってこと)
もちろんこれだけでは 100 人中 100 人がちゃんと利用できるとは限りません。が、実装上問題があるといった状態ではなくなります。(例としてはユーザーがメールアプリからリンクを開いて実際はアプリ内ブラウザであるが、Chrome だと思い込んでいるなど)
ちゃんと実装していれば遠回りにならないで済むのでエラーハンドリングをちゃんと行い、どのエラーがどういった場合に起こるかちゃんと仕様やドキュメントを読んで理解しておきましょう。
おわり