以前開発していたExpo製のアプリでService Workerを使用することになったのですが、Androidでは問題なく動作するのに対して、iOSではService Workerが動作せずハマりました。
iOSのWKWebViewではデフォルトでService Workerが使用できないようになっているのが原因だったようで、わかってしまえば簡単ですが、WKWebViewでService Workerを使用するための設定や手順で躓いた点をまとめました。
忙しい人向けのまとめ
-
app.json(もしくはapp.config.js)のexpo.ios.infoPlist.WKAppBoundDomainsにService Workerを使用したいサイトのFQDNを記載する -
expo buildやexpo publishでビルドしたアプリをインストールすることでService Workerを使用することができる -
expo startを使用してExpo Go経由でインストールしたアプリではinfoPlistの設定は反映されない
iOSにおけるWebView
大前提として、iOSのWebViewにはWKWebViewとSFSafariViewControllerがあり、Service Workerを使用できるバージョンやカスタマイズできるポイントが異なります。
(UIWebViewは非推奨なので除外してます)。
| WKWebView | SFSafariViewController | |
|---|---|---|
| Service Worker | iOS 14.0から使用可能 | iOS 11.3から使用可能 |
| 見た目のカスタマイズ | 自由にカスタマイズ可能 | 一部しか変更できない |
| Expo上でのパッケージ | react-native-webview | expo-web-browser |
Service Worker
iOSのSafariではiOS 11.3からService Workerがサポートされていますが、WKWebViewではiOS 14.0からでないと使用できず、使用するための設定が必要になります(この後説明します)。
対してSFSafariViewControllerでは、基本的にSafariと同等のAPIを使用することができるため、iOS 11.3からService Workerを使用することができます。こちらは特に設定などは必要なく、デフォルトでService Workerを使用することができます。
見た目のカスタマイズ
WKWebViewは1つのコンポーネントとして自由にカスタマイズできますが、代わりにブラウザバックボタンなどは自前で実装する必要があります。
SFSafariViewControllerは画面いっぱいにオーバーレイされるように表示されます。ブラウザバック/フォワードボタンや更新ボタンなどは標準で実装されていますが、カスタマイズ出来る部分が限られています。
Twitterのアプリ内ブラウザはSFSafariViewControllerで実装されているらしく、あのイメージです。
今回のアプリでは
iOSは14.0以降がサポート対象、かつWebViewの見た目を細かくカスタマイズしたいという要件があったため、WKWebView(react-native-webview)を採用する運びとなりました。
WKWebViewではデフォルトでService Workerが使用できない
WKWebViewではiOS 14.0からService Workerが使用可能と書きましたが、デフォルトでは使用できず、Service Workerを使用するための設定が必要となります。
この設定を行うファイルが、iOSアプリ開発で出てくるinfo.plistです。
詳しくは説明しません(というかわからない)が、info.plistはiOSアプリにおける設定ファイルです。(AndroidアプリでいうところのAndroidManifest.xmlと同じような位置付けだと理解してます。)
WKWebViewでService Workerを使用するには、info.plistにWKAppBoundDomainsを記載する必要があります。
WKAppBoundDomains
簡単に言うと、WKWebViewでブラウジングするときに信頼できるサイトのドメインリストです。
WKAppBoundDomainsでサイトのドメインを指定して、その中であればService Worker並びにブラウザの強力なAPIが使えるよ、というものです。
最大10個まで指定することができて、指定したいサイトのFQDNを記載することで機能します(スキーム付きで指定していて機能しなくてハマった)。
例:https://hogehoge.fugafuga.com の場合hogehoge.fugafuga.comを設定する
なぜそんな設定が必要なのか
WebKitの公式ドキュメントによると
多くのアプリケーションは、アプリ内ブラウジングと呼ばれる、ユーザがアプリを離れることなくウェブサイトを表示する便利な方法として、WKWebViewを使用しています。これは素晴らしいユーザー体験を提供しますが、WKWebViewを使用する開発者が利用できる強力な機能により、ホスティングアプリはアプリ内で訪問するすべてのサイトにわたってユーザーを監視することができます。
JavaScript インジェクション、イベントハンドラ、その他の API などの強力な WKWebView 機能は、アプリケーションまたはユーティリティフレームワークによって、ユーザーに関する個人情報を収集および集計しようとする既知のトラッカーと通信するための侵入的な方法で使用されることがあります。これらの手法により、ユーザーがどの画像で一時停止したか、どのコンテンツをコピー/ペーストしたか、スクロール中にページのどのセクションに到達したかが明らかになる可能性があります。
iOS 14.0とiPadOS 14.0では、開発者がユーザーをトラッキングリスクにさらすことなく、アプリ内ブラウジング体験を提供し続けることができるようにしたいと考えています。本日、私たちはApp-Bound Domainsを発表します。これは、オプトインの新しいWKWebView技術で、ユーザーにより高いプライバシーを提供することによってアプリ内ブラウジングを改善するものです。
(機械翻訳しただけなので読みにくくても許して)
とのことです。
色々書いてありますが、要は「WKWebViewでブラウザの機能をフルを使えるとセキュリティ的に良くないので、アプリ側で信頼できるドメインを指定して、そこでなら使えるようにしよう」ということです。
Expo側でWKAppBoundDomainsを指定する
WKAppBoundDomainsを設定すればいいことはわかりましたが、Expoプロジェクト内にはinfo.plistは存在しません(ベアワークフローを除く)。
代わりに、Expoアプリの設定ファイルであるapp.json上にinfo.plist相当の設定を記載することができます。
Expoの公式ドキュメントによると、expo.ios.infoPlistがinfo.plistに相当するようです。
app.jsonのexpo.ios.infoPlist.WKAppBoundDomainsにService Workerを使用したいサイトのFQDNを記載します。
https://hogehoge.fugafuga.com というサイトの場合は、下記のようになります。
{
"expo": {
...
"ios": {
...
"infoPlist": {
"WKAppBoundDomains": [
"hogehoge.fugafuga.com"
]
}
}
}
}
また、WebViewの設定でlimitsNavigationsToAppBoundDomainsをtrueにすることで、WKAppBoundDomainsに設定されたドメインのみにアクセスを制限することもできます。
<WebView limitsNavigationsToAppBoundDomains={true} />
ちなみにinfoPlistの項目に
(object) - スタンドアロン アプリのネイティブ Info.plist に追加する任意の設定の辞書です。他のすべてのExpo固有の設定より前に適用されます。他の検証は行われないので、App Store から拒否されるリスクはありますが、ご自身の責任で使用してください。
という恐怖の一文があります。App Storeへ提出する際にリジェクトを食らう可能性があると言っていますが、この方法でWKAppBoundDomainsを設定したアプリがApp Storeの審査を通過しているので問題はないはずです。
いざビルド
expo startでアプリをビルドしてみるもService Workerが動きません。
設定を間違えたか?と思い見直してみるも間違っているところはない模様。
expo startではinfoPlistの設定が反映されない
調べた結果、expo startでビルドしたアプリではinfoPlistの設定が反映されないようです。
公式ドキュメント上からはそのような記載を見つけられなかったのですが、よくよくinfoPlistの項目を見てみると
(object) - スタンドアロン アプリのネイティブ Info.plist に追加する任意の設定の辞書です。他のすべてのExpo固有の設定より前に適用されます。他の検証は行われないので、App Store から拒否されるリスクはありますが、ご自身の責任で使用してください。
確かにスタンドアロンアプリのと書いてあります。
スタンドアロンビルドする
Expoでスタンドアロンビルドするには
expo buildexpo publish
を使用します。
expo startは簡易ビルドのようなもので、ビルドしたアプリは「Expo Go」というExpoクライアントアプリ上で動作します。ホットリロードが可能だったりする代わりに、上述したような一部の機能が制限されます。
対してexpo buildでは、実際にスマホにインストールするのと近い形式でアプリをビルドすることができます。その代わり一回のビルドにはそこそこ時間がかかります(ビルドの待機時間にもよりますが、Expoサーバ上でビルドする場合は15~30分程度かかりました)。
スタンドアロンビルドしたアプリでは問題なくService Workerが動作することを確認できました。