53
63

More than 5 years have passed since last update.

iOS WKWebView ネイティブとローカルJavascript連携

Posted at

はじめに

ネイティブとローカルのJavascript間で値の受け渡しがやりたかったので調査しました
基本ローカルではなくサーバ側にJavascriptがある場合でも同じコードで動くはずです
Swift4, iOS9以降で動作確認しています

HTMLファイル構成

以下のようにしました。
注意として、ファイル構成はこれでなければいけないということはではありません
ディレクトリ名などわかりやすい名前をつけていただければと思います

WebPage
  |-css
  |  -style.css
  |-javascript
  |  -js.js
   -index.html

画像だと以下の感じとなります
スクリーンショット 2017-12-16 16.09.13.png

HTMLのソース

<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width">
        <link rel="stylesheet" type="text/css" href="style.css">
        <title>ページタイトル</title>
    </head>
    <body>
        <h1 id="title">タイトル</h1>

        <script type="text/javascript" src="js.js"></script>
    </body>
</html>

cssやjavascriptの読み込みが./css/〜./javascript/〜となっておらず、同階層として扱っている点に注意してください
調べていないので間違っているかもですが、ネイティブでHTMLを読み込む際、実際のディレクトリ階層とは異なり、同階層として読み込まれているためなのかなと思っています
スクリーンショット 2017-12-16 17.35.48.png

WKWebViewを使う準備

WKWebViewを使うにはWebKitをimportする必要があります

import WebKit

ローカルファイル読み込み

// index.htmlのパスを取得する
let path: String = Bundle.main.path(forResource: "index", ofType: "html")!
let localHtmlUrl: URL = URL(fileURLWithPath: path, isDirectory: false)
// ローカルのHTMLページを読み込む
webView.loadFileURL(localHtmlUrl, allowingReadAccessTo: localHtmlUrl)

Javascript → ネイティブ

宣言にWKScriptMessageHandlerを追加

class ViewController: UIViewController, WKScriptMessageHandler {

WKScriptMessageHandlerプロトコルのメソッドを実装

以下のuserContentControllerメソッドでJavascriptから値を受け取れます

func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {

}

WKUserContentControllerを設定

let webCfg: WKWebViewConfiguration = WKWebViewConfiguration()
let userController: WKUserContentController = WKUserContentController()
userController.add(self, name: "callbackHandler")
webCfg.userContentController = userController

webView = WKWebView(frame: self.view.bounds, configuration: webCfg)

ポイントはuserController.add(self, name: "callbackHandler")部分で、
ここで設定した"callbackHandler"がJavascriptから呼ばれると
WKScriptMessageHandler プロトコルのuserControllerメソッドが実行されます

userContentControllerの中身を実装

func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
    if(message.name == "callbackHandler") {
        print("\(message.body)")
    }
}

message.bodyにJavascriptから渡ってくる値が格納されています

Javascript側のコード

webkit.messageHandlers.<定義した文字列>.postMessage()
でネイティブ側に値を渡せます。今回は定義した文字列が"callbackHandler"なので以下のようになります

webkit.messageHandlers.callbackHandler.postMessage("Message from Javascript");

ネイティブ → Javascript

WKWebViewのevaluateJavaScriptメソッドを使います

宣言にWKNavigationDelegateを追加

class ViewController: UIViewController, WKNavigationDelegate {

処理の委譲先を指定

webView.navigationDelegate = self

WKNavigationDelegateの didFinish メソッドを実装

func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
    // Javascriptに渡す値
    let param: String = "jsCallTest"
    // Javascript側で実行する関数
    let execJsFunc: String = "test(\"\(param)\");"
    webView.evaluateJavaScript(execJsFunc, completionHandler: { (object, error) -> Void in
        // jsの関数実行結果
        // js側で戻り値を返すこともできる
    })
}

didFinishで行なっている理由は、HTMLのロードが終わってからでないとJavascriptのコードを実行できないためです
HTMLロード前に実行すると、Javascriptのメソッドが見つからず、evaluateJavaScriptのcompletionHandlerでerrorが返ってきます

Javascript側のコード

function test(val) {
    // テスト用に id タイトルを変更
    var h1 = document.getElementById("title");
    h1.textContent = val;
}

以上でローカルのJavascriptと双方向で値の受け渡しができるようになりました

最後に

今回のコードはこちらにおいています

53
63
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
53
63