環境
- Swift 4.2
- iOS 12.1
- Xcode 10.1
方法
JS 部分は document.documentElement.innerHTML
としてます。
1. evaluateJavaScript(_:completionHandler:) を使う
webView.evaluateJavaScript("document.documentElement.innerHTML") { value, error in
print(value as? String)
}
一番良く見るやり方。
このコードを実行する場合、読み込みが完了した WKNavigationDelegate の webView(_:didFinish:) 内などで実行する。
2. evaluteJavaScript を同期的に
UIWebView の stringByEvaluatingJavaScript(from:) 的な感じで。
extension WKWebView {
@discardableResult
func evaluate(javaScript script: String) -> String? {
var result: String?
var isCompletion: Bool = false
self.evaluateJavaScript(script) { value, _ in
result = value as? String
isCompletion = true
}
while !isCompletion { RunLoop.current.run(mode: .default, before: Date() + 0.1) }
return result
}
}
値が取得できるまで 0.1 秒ずつ待って、取得できたら値を返す。っていうイメージです。
同期処理なので、もちろん重い処理などは特に工夫したほうがいいですね。
DispatchSemaphore
で実現しようとすると、デッドロックが発生しうまくいきません。
そもそも evaluateJavaScript(_:completionHandler:)
は(UI系の操作も可能なため)メインスレッドでの実行を求められ、引数の closure もメインスレッドで実行されるため。
3. WKScriptMessageHandler を使う
これは日本語の解説記事があんまりなかったイメージ。
let js = """
var html = document.documentElement.innerHTML;
webkit.messageHandlers.getHtml.postMessage(html);
"""
let script = WKUserScript(source: js, injectionTime: .atDocumentEnd, forMainFrameOnly: true)
webView.configuration.userContentController.addUserScript(script)
webView.configuration.userContentController.add(self, name: "getHtml")
extension ViewController: WKScriptMessageHandler {
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
print(message.name) // "getHtml"
print(message.body) // HTML全文
}
}
気をつけるべきポイントとしては、上記コード例でいう2箇所の getHtml
の部分は一致してなければいけません。
この stack overflow の回答 のように、 extension を用意すると便利かもしれません。
参考
- https://qiita.com/ngyoi/items/ec580a2f9365d18b729a
- https://qiita.com/wassen/items/fc1aaffa16a49cf6ea8b
- https://stackoverflow.com/questions/24049343/
- https://stackoverflow.com/questions/34751860/
他にも方法があれば教えてください。
間違いのご指摘もお願いします!