Mais conteúdo relacionado Semelhante a iOS WKWebViewの魔改造 - iOSDC 2018 (20) iOS WKWebViewの魔改造 - iOSDC 20185. • 高速DOM (Document Object Model) 操作
• WKWebView内動画をAVPlayerでバックグラウンド再生
• WKWebView内ジェスチャへのアクセスと操作
• JavaScript -> Swift Tips
WKWebView
19. WKWebView + Mutation Observer
• DOM要素の変更(削除,非表示,置換等)をレンダリングの前に行える
• 要素がDOMツリーに追加された直後に EventListener をセットでき
る
20. WKWebView + Mutation Observer
• DOM要素の変更(削除,非表示,置換等)をレンダリングの前に行える
• 要素がDOMツリーに追加された直後に EventListener をセットでき
る
• すべてのDOM要素を監視・非同期操作してもパフォーマンスに大
きな影響は見られない
Notas do Editor WKWebViewの魔改造というタイトルで話します。
自己紹介 福山慎吾といいます。
IOSDCに参加するのは2回目ですが、
こういう場で話すことは初めてなのでよろしくお願いします。 今は楽天でROOMというアプリの開発に携わっています。
これは楽天市場のショッピングSNSです。
欲しいものや、購入したものを投稿をして
それを見たほかの人がその商品を購入すると、投稿した人も買った人も楽天ポイントがもらえるというアプリです。
最近Texture 旧名AsyncDisplayKitを導入し始めたので、現時点ではメイン画面だけスクロールがぬるぬる動くと思います。よかったら試してみてください。
今回、話す内容はこのアプリではなく、 このアプリ。
2014年からiOSのWebブラウザアプリを趣味で作っています。
ちょうどこのとき、iOS 8でWKWebViewも公開されました。
当時のWKWebViewはできないことばかりで、たいへん苦労しました。
今回はその中で、今でも使えるWKWebViewの技術を紹介します。
内容はこちら
高速DOM、Document Object Model 操作。
これによってリアルタイムにコンテンツブロックやスタイル操作を行えるようになります。
WKWebView内の動画をカスタマイズしたAVPlayerでバックグラウンド再生できるようにします。これに関してはサンプルアプリも後ほど公開します。
WKWebView内部のジェスチャへのアクセスと操作。
あることをするとWKWebView内にある独自のUIGestureRecognizerを扱えるようになります。
そして最後に少し、JavaScriptからSwiftへの通知で役立ちそうなサンプルを紹介します。
WKWebView
ここ最近、再びWKWebViewが注目され始めました。
なんでだと思いますか?
それは、、、
UIWebView.
iOSがまだiPhone OSと呼ばれていた時代、
2008年から10年も存在し続けているAPIです。
それがこのたび iOS 12で、 公式に非推奨・廃止予定となりました。
今までは非推奨となってはいなかったものの、AppleからもWKWebViewへ移行するように促すドキュメントも見られました。
[アンケート]
興味本位に少し聞いてみたいんですけど、
この会場の中で、今取り組んでいるプロジェクトでUIWebViewを使っている、
または移行中というかた。少し手を上げていただけますでしょうか?
Result: more than 30%
次にWKWebViewとUIWebViewとの比較を簡単に紹介します。
レンダリング速度、JavaScriptの実行速度が高速化されています。
アプリ自体とWKWebView、ネットワーク処理はそれぞれ別のプロセスで動作しているため、どれかにセキュリティ問題が生じても全てに影響しにくくなっています。
プロセスが分離されているため、WKWebViewのプロセスがクラッシュしたとしても、アプリ自体はクラッシュしません。
iOS Google ChromeがUIWebViewからWKWebViewへ移行した際にクラッシュ率が70%減少したという記事もあります。 では、SwiftとJavaScriptを使ってゴリゴリしていきます。
SwiftからWKWebViewへJavaScriptを注入する方法の一つに
evaluateJavaScript:completionHandler:
というメソッドもありますが、今回は別の方法を使います。
Webページのコンテンツ読み込み前、もしくは読み込み後に毎回自動でJavaScriptを実行する
WKUserScriptを使います。
コードはこんな感じで
WKWebViewConfigurationで設定します。
注入のタイミングは2パターン。
Documentが読み込まれた後でかつコンテンツの読み込み前。
コンテンツの読み込み後かつ画像などのサブリソースのロード前です。 ここで何を実行するのか。
それがこれから紹介するもの。
WKWebViewと組み合わせると強力でいろいろできるJavaScript APIがあります。
それが、 Mutation Observerというものです。
このAPIはDOMの変更を監視するものです。
通常のWebページで使われることは稀かれもしれませんが、
不特定多数のWebページを扱うWebブラウザで力を発揮します。 このように設定ができ、DOM変更に加えて、特定のattributes変更のみを監視することもできます。
Documentを監視対象にすることで<html>タグを含む全ての変更を監視できます。 変更結果はリアルタイムに配列として帰ってきます。
ここで特定の要素をフィルターして、変更を加えていきます。
モダンなJavaScriptの書き方には、
キャッチアップしていないので勘弁してください。 WKWebViewとMutation Observerを組み合わせることで得られるメリットは、 DOM要素の変更、削除、非表示、置換などをレンダリングの前に行えます。
そして、要素がDOMツリーに追加された直後に
JavaScript APIのEventListenerを追加でき、
イベントを監視することができます。 ベンチマークを取ったわけではありませんが、
すべてのDOM要素を監視・非同期操作してもパフォーマンスに大きな影響は見られませんでした。
同期操作では重たくなるので非同期
setTimeout、最近ではPromiseでしょうか。
を使います。 WKWebViewとMutationObserverの活用例 iOS 11でコンテンツブロックがWKWebViewにも導入されましたが、
それより前のiOSでも有効な手法です。
もちろんiOS 11のコンテンツブロックと組み合わせることでより強力なコンテンツブロックが可能になります。
レンダリングの前にスタイルを操作できるため、
ユーザーから見てもスタイルが変わる瞬間に気づくことはありません。 そしてEventListenerを追加してイベントの即時監視ができます。
これは次に話すことに関連してきます。 WKWebView内動画のバックグラウンド再生方法について話します。 通常WKWebView内で動画を再生すると、アプリ起動中のみ再生を行えます。
一方 AVKitのAVPlayerでは ホーム画面、
ほかアプリ使用中、
デバイスロック中
に動画の音声が継続して再生できます。
さらにiPadではPIP、Picture in Pictureを使って
動画をホーム画面や他のアプリにいるときでも小窓で再生することが可能です。 AVPlayerで再生できればなんでもできそうです。
単純にWKWebViewの動画からURLをぽんっと渡せば良さそうですが、
通常の動画サイトはなんらかの対策をとっており、そうはさせてくれません。 動画URLの取得 src属性にあるURLをコピーすれば良さそうですが、 これは通常ダミーファイルもしくは空となっています。
しかし、本物の動画URLが現れる瞬間があります。 それがplayイベント発火時です。
このときのURLをSwift側のAVPlayerへ渡します。
ちなみにAndroidのWebViewはBlob URLでの再生に対応しており、
この方法ではうまくいきません。
そのまえに、 WKWebViewのデフォルトプレイヤーが出てくるのを防ぐ必要があります。
WKWebViewConfigurationには
allowsInlineMediaPlaybackというプロパティがあり、
初期値はiPhoneではfalse、iPadではtrueとなっています。
この値をtrueにして、インライン再生の設定を行えば、全画面化することなくWebページの上で再生できます。
iOS 10より前への対応は<video>タグにwebkit-playsinlineという属性をつけると大丈夫です。
再生を開始した<video>タグは用済みなので即座に停止させます。
WKWebView内の動画バックグラウンド再生
のまとめ Mutation Observerで<video>タグ追加時に捕まえる
EventListenerで<video>タグのplayイベントを監視する WKScriptMessageHandlerで本物の動画URLをSwift側へ通知する デフォルトプレイヤーの起動を阻止し、バックグラウンド再生やPIPの設定をしたAVPlayerで再生
サンプルアプリは後で公開します。 WKWebView内ジェスチャの操作 -> まず、呼び水としてWKWebViewにpanジェスチャをつけます。 UIGestureRecognizerDelegate
gestureRecognizer(_:shouldRecognizeSimultaneouslyWith:)
で取り出します。
呼び水にしたジェスチャと衝突した他のWKWebView内ジェスチャがここで姿を現します。 WKWebView自体にpanジェスチャをつける UIGestureRecognizerDelegateで取り出す。
そうすると、次のようなジェスチャが網にかかります。 見慣れないものばかりですね。
これらは私達が普段使うジェスチャのサブクラスだったりするので、
親クラスにキャストすることで扱うことができます。
無効にしたり、プロパティをいじったり、特定のジェスチャをなにかの目印にするのもいいと思います。 活用例です。
特定のLongPressGestureを捕まえて
WKWebView内リンクの長押し時間を変更したり、 WKWebView上のスクロール可能領域を操作中かどうかを判定できます。 次はWKScriptMessageHandlerの活用例を紹介します。
SwiftやObjective-Cで言うところの黒魔術のJavaScript版です。
WKScriptMessageHandlerはJavaScriptからSwiftへ通知を行うものです。 Document.cookieにgetterとsetterを生やします。
もともとの機能は保たれており、
Cookieが追加された瞬間にSwift側へ値とともに通知する仕組みです。
Cookieに限らず様々なJavaScriptのプロパティを監視できるようになります。
Ajax通信リクエストの監視、すり替え
XMLHttpRequestをオーバーライドする形で
既存の働きの前にURLフィルタをかけ、ブロックしたい通信をすり替えています。 最後に
こちらが動画再生のサンプルアプリです。
[時間があればデモ]
[PR]