以前開発していた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 build
expo publish
を使用します。
expo start
は簡易ビルドのようなもので、ビルドしたアプリは「Expo Go」というExpoクライアントアプリ上で動作します。ホットリロードが可能だったりする代わりに、上述したような一部の機能が制限されます。
対してexpo build
では、実際にスマホにインストールするのと近い形式でアプリをビルドすることができます。その代わり一回のビルドにはそこそこ時間がかかります(ビルドの待機時間にもよりますが、Expoサーバ上でビルドする場合は15~30分程度かかりました)。
スタンドアロンビルドしたアプリでは問題なくService Workerが動作することを確認できました。