WKWebViewを使ってNativeとWebページで情報をやり取りする方法
iOS8から使えるようになったWKWebViewはUIWebViewよりもJavaScriptとの連携が強化されています。 WKWebViewを使うことでアプリ側独自のJavaScriptを既存Webページに読み込ませ、実行させるということが簡単にできるようになりました。 (ブラウザでユーザースクリプトを実行させるというようなことがNativeでもできる) WKWebViewを使ったユーザースクリプト実行方法のサンプルを紹介します。
ATS(App Transport Security)を無効にするInfo.plistの設定
iOS9からhttpでリクエストを送るとhttpsに置き換わってしまいます。 テストをするためにinfo.plistに下記を追加してhttpsに置き換わるのを無効にします。
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
Swiftコード
WKWebViewを使うために「WebKit Framework」インポートします。 Swift上でJavaScriptコードを文字列として記述するのは大変なので別ファイルで用意(test.js)しました。 ユーザースクリプト(WKUserScript)を作成する際のinjectionTimeでJavaScriptの注入タイミングを指定できます。 JavaScriptで実行した結果を受け取りたい場合はWKUserContentControllerのaddScriptMessageHandlerで設定します。 WKWebViewのデリゲートWKScriptMessageHandlerのuserContentControllerメソッドを追加します。 このuserContentControllerメソッドでJavaScriptからのメッセージが受け取れます。 下記のコードでは受け取ったメッセージをコンソールに出しています。
import UIKit
import WebKit
class ViewController: UIViewController, WKNavigationDelegate, WKScriptMessageHandler {
var webView : WKWebView?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
var jsSource = ""
// JavaScriptコードはtest.jsという名前で別ファイルに用意します
let path = NSBundle.mainBundle().pathForResource("test", ofType: "js")!
if let data = NSData(contentsOfFile: path){
jsSource = String(NSString(data: data, encoding: NSUTF8StringEncoding)!)
}
// WKUserScriptを作成、injectionTimeにJavaScriptを実行するタイミングを指定します
let userScript1 = WKUserScript(source: jsSource, injectionTime: WKUserScriptInjectionTime.AtDocumentEnd, forMainFrameOnly: true)
// JavaScript側の実行結果などを受け取りたい場合はコールバックを設定します
let controller = WKUserContentController()
controller.addUserScript(userScript1)
controller.addScriptMessageHandler(self, name: "callbackHandler")
let configuration = WKWebViewConfiguration()
configuration.userContentController = controller
self.webView = WKWebView(frame: view.bounds, configuration: configuration)
self.view = self.webView
let urlString = "https://www.mitsue.co.jp/"
let encodedUrlString = urlString.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet())
let url = NSURL(string: encodedUrlString!)
let request = NSURLRequest(URL: url!)
self.webView!.loadRequest(request)
}
func userContentController(userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage) {
// addScriptMessageHandlerで指定したコールバック名を判断して処理を分岐させることができます
if(message.name == "callbackHandler") {
print(message.body)
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
JavaScriptコード
jQueryを使っていないサイトの場合に備えてjQueryを読み込ませる処理を行っています。 このスクリプトでは「www.mitsue.co.jp」内のお知らせとキャンペーン情報を取得し、Native側に送っています。 JavaScriptで「webkit.messageHandlers.Native側で設定したコールバック名.postMessage("Native側に送りたい情報");」とするだけでNative側にメッセージを送ることができます。
(function (callback) {
// jQueryを使っていないサイトの場合用にjQueryを読み込みます
var script = document.createElement("script");
script.setAttribute("src", "//code.jquery.com/jquery-2.0.0.min.js");
script.addEventListener('load', function() {
var script = document.createElement("script");
script.textContent = "(" + callback.toString() + ")(jQuery.noConflict(true));";
document.body.appendChild(script);
}, false);
document.body.appendChild(script);
})(function ($) {
$('div.news').each(function() {
// www.mitsue.co.jp内のお知らせとキャンペーンの情報を取得してNative側に送ります
// webkit.messageHandlers.Native側で設定したコールバック名.postMessage("Native側に送りたい情報");
webkit.messageHandlers.callbackHandler.postMessage($(this).find('a').html());
});
});
実行結果
eachの中で連続して送ってもNative側では正常に受け取ることができます。
この技術を使うことで既存Webページ上の必要な情報のみをNative側に取り込み、アプリ独自の見せ方をさせるというようなことができるようになります。