WKWebViewのカスタムUserAgentを設定する方法
Table of Contents
UIWebViewがiOS13から非推奨になり、WKWebViewへの移行が進んでいます。 しかし、移行は一筋縄では行かないことが多いです。私の場合は、User Agentの設定で、かなり苦戦しました。 そこで今回は、WKWebViewにUser Agentを設定する方法をまとめます。
WKWebViewのUserAgent設定方法 #
それでは、WKWebViewでの設定方法をまとめていきます。いくつかあります。
- UserDefaultsにセットする
- WKWebViewのカスタムUAにセットする
- デフォルトのUAの後ろにカスタム文字列を付ける方法
- Interface Builderで設定する
UserDefaultsにセットする #
これはUIWebView時代と変わりません。しかし、WKWebViewのインスタンスが生成される前にセットする必要があります。
var webView: WKWebView!
override func loadView() {
super.loadView()
// WKWebViewのインスタンスを作る前にUserAgentをセットする
setUserAgent()
webView = WKWebView()
webView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
self.view = webView
webView.navigationDelegate = self
}
override func viewDidLoad() {
super.viewDidLoad()
load()
}
private func setUserAgent() {
UserDefaults.standard.register(defaults: ["UserAgent": "CustomUserAgent"])
}
インスタンス生成前であればいつでも良いので、AppDelegateで実行するのも良いでしょう(許容されるならば)。
WKWebViewのカスタムUAにセットする #
UserDefaultsよりもかんたんなのが、WKWebViewのcustomUserAgent
に文字列をセットする方法です。
webView.customUserAgent = "CustomUserAgent"
これであれば、UserDefaultsを使わないだけでなく、インスタンスごとに個別のUserAgentを持つことができます。 複数のWebViewを使う要件であれば、こちらを使うのが便利でしょう。
アプリ名をsuffixとして付与する場合 #
WKWebViewConfiguration
を使用します。applicationNameForUserAgent
というプロパティにセットした文字列が、suffixとして付与されます。
UIWebViewとは異なる方法です。
var webView: WKWebView!
override func loadView() {
super.loadView()
// configにカスタムで付与したい文字列をセット
let config = WKWebViewConfiguration()
config.applicationNameForUserAgent = "CustomUserAgent"
// WKWebViewのインスタンス生成時に、configを渡す
webView = WKWebView(frame: .zero, configuration: config)
webView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
self.view = webView
webView.navigationDelegate = self
}
実際に、WKNavigationDelegate
のwebView(_:decidePolicyFor:decisionHandler:)
で、リクエストヘッダを見てみます。
Optional(["User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 12_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CustomUserAgent", ...])
確かに、カスタムのUserAgent文字列が、後ろに付与されていることがわかります。 このように、WKWebViewConfigurationを使うことで、suffixをつけられるようになりました。 WKWebViewは、storyboard・Interface Builderではなく、コードからインスタンスを生成するほうが、何かと都合がいいですね……。
Interface BuilderでカスタムUAをつけられるようになっていた! #
上記画像の通り、Interface BuilderのInspectorで、User AgentとApp Name(applicationNameForUserAgent)が設定できるようになっていました。上記の画像のように設定した場合、Xcode 11.3.1のシミュレーターでは、以下のようなUAになりました。
Mozilla/5.0 (iPhone; CPU iPhone OS 13_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) UISamplesIOS
これで、Interface Builderで定義したレイアウトを大きく変更せずとも、WKWebViewへ移行できますね!
JavaScriptの実行結果ではだめなのか #
WKWebViewにもJavaScriptの実行結果を取得できるメソッドが用意されていますが、その仕様はUIWebViewとは変わりました。 クロージャで返ってくるようになり、実行結果は非同期で取得することになります。
// 使用するAPI
func evaluateJavaScript(_ javaScriptString: String, completionHandler: ((Any?, Error?) -> Void)? = nil)
var webView: WKWebView? = WKWebView()
webView?.evaluateJavaScript("navigator.userAgent") { (userAgent, error) in
guard let originalUserAgent = userAgent as? String else { return }
// クロージャが実行されるまで、webViewインスタンスが必要なのでキャプチャしておく
webView = nil
// originalUserAgentを処理するなり、保存するなり
let customizedUA = originalUserAgent + "CustomUserAgent"
UserDefaults.standard.register(defaults: ["UserAgent" : customizedUA])
}
UserAgentを取得・セットするタイミングが不定でも問題ないのであれば、この方法でも大丈夫だと思います。 しかし、UIWebViewの実装のように順次実行されるわけではないので、UAが設定されるタイミングを制御できません。 UA設定のタイミングが重要な場合は、WKWebViewConfigurationを使うほうが安全です。
非同期実行が困るという場合、同期的に実行したい場合 #
アプリの都合から、非同期では困る場合もあると思います。その場合は、処理の待ち合わせを手軽に実装できる、DispatchSemaphore
を使用すると良いでしょう。DispatchSemaphore
についての詳しい記事は、以下になります。
非同期を同期的に待ち合わせる方法(ライブラリは使わず・Swift)
まとめ #
WKWebViewでUserAgentをカスタマイズする方法をまとめました。 UIWebViewと似ているようで、実際には全く異なるAPIなので、移行には苦戦することもあります。 一方で、WKWebViewConfigurationを使うことによりsuffixをつけやすくなったり、WKNavigationDelegate・WKUIDelegateを使って細かく挙動を制御できたりと、柔軟性も増しています。 ネイティブアプリではWebViewを酷使することは少ないですが、もしUserAgentを変更してWebViewを使用する際には、参考にしてもらえると幸いです。
おまけの振り返り: UIWebViewの頃 #
いまいちどUIWebView時代は、どのようにしてUser Agentを設定していたかを振り返ります。
UserDefaultsにセットしていた #
UIWebViewは、UserDefaultsに保存してあるUser Agentの値を使用します。
UserDefaultsに、UserAgent
のキーでStringが保存されていれば、その値を使用します。
実装の仕方は、このような感じ。
var webView: UIWebView!
override func viewDidLoad() {
super.viewDidLoad()
setUserAgent()
load()
}
/// UserAgentを設定する
private func setUserAgent() {
UserDefaults.standard.register(defaults: ["UserAgent": "CustomUserAgent"])
}
/// ページを読み込む
private func load() {
let request = URLRequest(url: URL(string: "https://google.com/")!)
webView.loadRequest(request)
}
では、UserAgentの変化を見てみます。
UIWebViewDelegate
のwebView(_:shouldStartLoadWith:navigationType:)
でURLRequestが取れるので、allHTTPHeaderFields
をprintしてみます。
func webView(_ webView: UIWebView, shouldStartLoadWith request: URLRequest, navigationType: UIWebView.NavigationType) -> Bool {
print(request.allHTTPHeaderFields)
return true
}
// カスタム値をセットする前のUserAgent
Optional(["User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 12_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148", ...])
// セットしたあとのUserAgent
Optional(["User-Agent": "CustomUserAgent", ...])
たしかにUserAgentが変更されたことがわかりました。
アプリ名をsuffixとして付与する場合 #
「UserAgentを全部変えたいわけじゃない、デフォルトUAの後ろにアプリ名をつけたいんだ」という要件は多いようです。UIWebViewでは、こうやっていました。
let originalUA = UIWebView().stringByEvaluatingJavaScript(from: "navigator.userAgent")!
let customizedUA = originalUA + " CustomUserAgent"
UserDefaults.standard.register(defaults: ["UserAgent": customizedUA])
JavaScriptの実行結果がStringで返ってくるので、その後ろに、任意の文字列をくっつけるだけで完成でした。
ここまで読んでくださってありがとうございます。 それでは失礼いたします。