杏z学習帳

Swift、Unityなどを中心に節操なく手を出してますまる

WKWebViewのUserAgentに追記をする(2019/09/02)

[Swift]WKWebViewのUserAgentに追記をする

ざっくりまとめ。

です。

環境

やりたいこと

WKWebView にデフォルトで設定されている UserAgent に対して追記をしたい(アプリ名とそのバージョンとか)。
まるっと書き直したいのではなくて、追記がしたい。(重要なので2回..)

UIWebView ...?知らない子ですね。

やりたいことを満たすのに個人的にイマイチだった方法たち

最終的に採用した方法は最後のあたりに記載しているので、
答えだけサクッと見たいかたはスクロールしてください(笑)

UserDefaults で設定していく

WKWebView UserAgent」 みたいなキーワードでググると最も出てくる方法ではないでしょうか。

UserDefaults.standard.register(defaults: [
    "UserAgent": "ここに使いたいUserAgentを書く"
])

UserDefaults のデフォルト値を設定するので application:didFinishLaunchingWithOptions: 内で行うことがまず大事。
設定した UserAgent がまるっと採用されるので、今回やりたいことの追記をするという意味ではデフォルトのUserAgentを取る必要がある。
そのために WKWebView に対して navigator.userAgent という js を発行して結果を得る必要があるのだけれど、これが非同期なので同期的にとれるような工夫が必要になってくる。
これが少しめんどくさい。。

WKWebView.customUserAgent で設定する

上記の方法だと、全ての WebView で適用されてしまうので、個別に扱いたい場合なんかはこちらが有効になってくるでしょうか?

let webview = WKWebView(frame: .zero)
webview.customUserAgent = "ここに使いたいUserAgentを書く"

この方法でも、指定された UserAgent がまるっと使われるので、UserDefaults の方法と同じくまずはデフォルトのUserAgentを取得してくる必要があります。

let webview = WKWebView(frame: .zero)
webview.evaluateJavaScript("navigator.userAgent", completionHandler: { (result, error) in 
    guard let ua = result as? String else {
        return
    }
    webview.customUserAgent = ua + " 追記したいUserAgent"
    webview.load(req: ....)
})

雑に書くとこういう感じです。
うーん。。なんかイケてないですね。

この上記2つの方法で僕が最も問題・課題だとおもったものは、
"navigator.userAgent" を実行して結果をえるまでがものすごく遅いということです。
上記2つの方法だと指定した UserAgent がそのまま使われるため、WebView が持ってる UserAgent に何かを追記した UserAgent を使いたいとなると、
一旦 WebView が持ってる UserAgent を取得する必要があるので、この js 発行がどうしても避けて通れない。
そしてそれがすごくおそい。
どれぐらいかというと、シミュレータでも 0.1s-0.3s ほど。古めの端末だと 0.5s ほど。どういう条件なのかはわからないですが、ひどい時は 1s を超えてるくることも。
(3s以上とかっていうときもありましたが、これは流石に異常すぎるので僕が何かしてたかもしれません)

ちなみに、計測の仕方はこんな感じです。

let start = Date()
web.evaluateJavaScript("navigator.userAgent", completionHandler: { (_, _) in
    print(Date().timeIntervalSince(start))
})

UX的に速度改善をつとめてて 0.1s でも縮めたい!とか思っているところ、こんなに時間がかかっていては話にならないというか。
アプリ起動時に同期的に処理してもし 3.0s とかかかったらとか考えると怖すぎます。

最終的に採用した方法

ということで真打登場です(笑)

WKWebViewConfiguration.applicationNameForUserAgent で設定する

config のこのプロパティは今までのものとは違っていて、設定した文字列がまるっと UserAgent になるのではなく、
WebView が持ってるデフォルトの UserAgent に設定された文字列を追記して実行してくれる。。
という、今回やりたかったことをそのまま実現してくれるやつなのです。
なので、わざわざ js を発行して取ってくる必要がありません。追記したい文字列をそのまま設定するだけでいいのです。

let config = WKWebViewConfiguration()
config.applicationNameForUserAgent = "MyApp"
let webview = WKWebView(frame: .zero, configuration: config)

これで何かをロード実行すると

Mozilla/5.0 (iPhone; CPU iPhone OS 12_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) MyApp

となって MyApp が無事に追記されています!!
UserAgent いじりたいときって本当にまるっと変えたいとかって稀有だとおもうので、大抵のケースはこれ使ったほうが良いかも。
プロパティ名的にはアプリ名みたいな感じですが、設定はただの文字列なのである程度は自由に設定できるはず(笑)

優先度の話

ちなみに上記のものを複合的に設定した場合どうなるのか? 雑に優先度を書くと

Webview.customUserAgent > UserDefault.register(defaults: [:]) > WKWebViewConfiguration.applicationNameForUserAgent

という感じです(左のほうが優先度が高い💪)
ただし!
customUserAgent あるいは UserDefaults どっちかの方式が設定されている場合、applicationNameForUserAgent は設定しても無視されます。
applicationNameForUserAgent はあくまでも webview が持ってるデフォルトの UserAgent に対して追記するということらしいです。

参考