Yappli Tech Blog

株式会社ヤプリの開発メンバーによるブログです。最新の技術情報からチーム・働き方に関するテーマまで、日々の熱い想いを持って発信していきます。

WebViewでYouTubeが再生できない(153エラー)時の対処法

iOSチームの加藤です。
今回は、WKWebViewを利用してYouTubeの再生機能を実装する際につまずいた箇所がありましたので共有したいと思います。

概要

iOSアプリ内でYouTubeを再生する場合、WKWebView上でIFrame Player APIを利用するのが一般的です。

基本的な実装方法は公式のサンプルに記載されており、下記のようにWKWebViewの loadHTMLString を利用してHTMLを読み込むことで再生が可能でした。

しかし、以前はこの実装で問題なく再生できていたのですが、ある時から添付画像のような エラーコード:153 が表示され、再生できない事象が発生しました。

▼ 修正前のコード(再生できない)
func loadLocalHTML() {
    webView.loadHTMLString(html(), baseURL: nil)
}

func html() -> String {
    let code = "1pwnSfO9bg4"
    let html = """
    <!DOCTYPE html>
    <html>
    <head>
        <meta name="viewport" content="width=device-width">
    </head>
    <body style="margin:0">
        <div id="a"></div>
        <script>
            var a = document.createElement('script');
            var b = document.getElementsByTagName('script')[0];
            a.src = 'https://www.youtube.com/iframe_api';
            b.parentNode.insertBefore(a, b);
            
            var c;
            function onYouTubeIframeAPIReady() {
                c = new YT.Player('a', {
                    videoId: '\(code)',
                    width: '300',
                    height: '150',
                });
            }
        </script>
    </body>
    </html>
    """
    return html
}

原因調査

まずはリファレンスのイベント(エラーコード)を確認しましたが、ここには「153エラー」に関する記載がなく、原因の特定に時間がかかってしまいました。

調査を続けたところ、公式ドキュメント内の「必要な最低限の機能」という項目に答えがありました。

この構成では、プレーヤーはアプリにバンドルされている HTML ファイルに読み込まれます。この HTML ファイルを読み込むときに baseUrl パラメータを設定すると、Referer が設定されます。

つまり、ローカルのHTMLを読み込む際、baseURL が未設定(nil)だとリクエスト元の身元が不明となり、YouTube側のセキュリティによってブロックされていたようです。

解決策

ドキュメントに従い、baseURL にアプリのバンドルIDを含むURLを設定するように修正したところ、無事に表示されるようになりました😄

▼ 修正後のコード(解決)
func loadLocalHTML() {
    // baseURLにダミーのURL(https://バンドルID)を設定する
    let bundleId = Bundle.main.bundleIdentifier ?? "com.example.app"
    let referrer = "https://\(bundleId)".lowercased()
    
    guard let referrerUrl = URL(string: referrer) else {
        return
    }
    // baseURLを指定して読み込む
    webView.loadHTMLString(html(), baseURL: referrerUrl)
}

func html() -> String {
    let code = "1pwnSfO9bg4"
    let html = """
    <!DOCTYPE html>
    <html>
    <head>
        <meta name="viewport" content="width=device-width">
    </head>
    <body style="margin:0">
        <div id="a"></div>
        <script>
            var a = document.createElement('script');
            var b = document.getElementsByTagName('script')[0];
            a.src = 'https://www.youtube.com/iframe_api';
            b.parentNode.insertBefore(a, b);
            
            var c;
            function onYouTubeIframeAPIReady() {
                c = new YT.Player('a', {
                    videoId: '\(code)',
                    width: '300',
                    height: '150',
                });
            }
        </script>
    </body>
    </html>
    """
    return html
}

まとめ

変更履歴等を確認すると、YouTube側の仕様やセキュリティ要件は定期的に更新されているようです。 アプリ側で急に再生できなくなるリスクを減らすためにも、こうした仕様変更にいち早く気付ける仕組みづくりが必要だと感じました。


ヤプリでは、エンジニアを募集しています。興味のある方はぜひご連絡ください!
open.talentio.com