WebViewJavascriptBridgeを使ってみた

#はじめに
[SwiftからPepperの音量を変更する](http://qiita.com/miyae/private/774330be89ba89877a71)を実行するにあたって、使用したWebViewJavascriptBridgeについてまとめました。
リンク先の記事にも記載されていることもありますが、ご了承ください。

#ライブラリのインストール
アプリに関しての説明記事にも記載してありますが、改めて記載しておきます。
CocoaPodsを使用して[WebViewJavascriptBridge](https://github.com/marcuswestin/WebViewJavascriptBridge)（ver6）をインストールします。以下のように`pod 'WebViewJavascriptBridge', '~> 6.0'`をPodfileに追加して`pod install`してください。（別のバージョンをインストールしたい場合は、'~> 6.0'の部分を変更してください。

```:Podfile
# Uncomment the next line to define a global platform for your project
# platform :ios, '9.0'

target 'WVJBSwift' do
  # Comment the next line if you're not using Swift and don't want to use dynamic frameworks
  use_frameworks!

  # Pods for WVJBSwift
  pod 'WebViewJavascriptBridge', '~> 6.0'

end
```
CocoaPodのインストール等についてわからない場合は、こちら[【Swift】CocoaPods導入手順](http://qiita.com/ShinokiRyosei/items/3090290cb72434852460)の記事等を参考にして導入してみてください。
※WebViewJavascriptBridgeはSwiftとJavaScriptの間でメッセージの送信を行うためのライブラリです。

#JavaScript側の設定について
WebViewJavascriptBridge用の関数を追加します。必ず必要なもののため以下の関数を忘れずに追加してください。

```js
function setupWebViewJavascriptBridge(callback) {
    if (window.WebViewJavascriptBridge) { return callback(WebViewJavascriptBridge); }
    if (window.WVJBCallbacks) { return window.WVJBCallbacks.push(callback); }
    window.WVJBCallbacks = [callback];
    var WVJBIframe = document.createElement('iframe');
    WVJBIframe.style.display = 'none';
    WVJBIframe.src = 'https://__bridge_loaded__';
    document.documentElement.appendChild(WVJBIframe);
    setTimeout(function() { document.documentElement.removeChild(WVJBIframe) }, 0)
}
```

Swiftとメッセージのやり取りをするための処理は以下ように上で登録した関数を呼び出した中に記述します。

```js
setupWebViewJavascriptBridge(function(bridge){
    // ここに記述
}
```

#Swift側の設定について
WebViewJavascriptBridgeをインポートしてください。

```swift:import
import WebViewJavascriptBridge
```
使用しているWebViewにHTMLページを読み込みます。

```swift:loadHtmlPage
        let htmlPath = Bundle.main.path(forResource: "WVJBQi2", ofType: "html")
        do{
            let appHtml = try String(contentsOfFile: htmlPath!, encoding: String.Encoding.utf8)
            let baseURL = URL.init(fileURLWithPath: htmlPath!)
            //URLの情報をWebViewに読み込む
            webView.loadHTMLString(appHtml, baseURL: baseURL)
        }catch{
            print("Html読み込みエラー")
        }
```
・1行目：読み込むHTMLファイルへのパスを取得
・3行目：HTMLファイルの内容を取得
・4行目：読み込むURLのパスを生成
・6行目：URLの情報をWebViewに読み込む

プログラム内で使用しているWebViewを渡してブリッジを作成します。

```swift
WebViewJavascriptBridge.init(forWebView:(WKWebView/UIWebView/WebView*)webview)
```
```swift:createBridge
    func createBridge(){
        self.bridge = WebViewJavascriptBridge.init(forWebView: mywebView)
    }
```


#Handlerについて
JavaScript側でregisterHandlerを使用して登録する場合は以下のように記述してください。

```js:registerHandler（Javascript）
bridge.registerHandler("ハンドラ名", function(data, responseCallback) {
  　　　　// 処理内容
}
```
ハンドラ名は任意です。Swift側で呼び出す際にここで設定した名前を使用してください。

上記のJavaScriptの処理を使用するためにSwift側でcallHandlerを使用します。

```swift:callHandler（Swift）
    self.bridge.callHandler("ハンドラ名", data: [JSへ渡す値]) {(responseData) in
        //処理内容
    }
```

例）"getVolume"というハンドラ名を使用する場合

```js:getVolume（JavaScript）
    bridge.registerHandler('getVolume', function(data, responseCallback){
        self.audioDevice.getOutputVolume().then(function(volume){
            responseCallback(volume)
        }, function(error){
            responseCallback(error)
        });
    })
```

```swift:getVolume（Swift）
    self.bridge.callHandler("getVolume", data: nil) {(responseData) in
        let volume = responseData as! Int
        self.myVolSlider.setValue(Float(volume), animated: true)
    }
```
上記の例の場合、
Swift側でcallHandlerして、JavaScript側の"getVolume"を呼び出します。
"getVolume"内のgetOutputVolume()で取得したvolumeをresponseCallbackでSwift側に渡します。
Swift側では受け取ったvolumeをIntに型変換してスライダーへ値の設定を行なっています。


Swift側でregisterHandlerを使用して登録する場合は以下のように記述してください。

```swift:registerHandler（Swift）
self.bridge.registerHandler("ハンドラ名"){(data: [JSへ渡す値], responseCallback) in
    //処理内容
}
```

```js:callHandler（Javascript）
bridge.callHandler("ハンドラ名", data, function(response) {
    // 処理内容
}
```


例）"endOfAnimatedSpeechEvent"というハンドラ名を使用する場合
以下の例は記事で紹介しておりませんが、記事用ではなく、実際に作成したアプリ内で使用しているもので、Pepperが何か話し終えると発生するイベントを捕捉しています。

```swift:endOfAnimatedSpeechEvent（Swift）
    self.bridge.registerHandler("endOfAnimatedSpeechEvent") {(data, responseCallback) in
        responseCallback!("アニメーションの停止")
    }
```
```js:endOfAnimatedSpeechEvent（JavaScript）
    bridge.callHandler('endOfAnimatedSpeechEvent', state, function(response) {
        log('ペッパーイベント発火時のresponse', response)
    })
```
上記の例の場合、
JavaScrip側でcallHandlerしてSwift側の"endOfAnimatedSpeechEvent"を呼び出します。
"endOfAnimatedSpeechEvent"内のresponseCallbackでメッセージをJavaScript側に渡します。
Javascript側では受け取ったメッセージ（response）をログに出力しています。

#おわりに
このライブラリですが、Swift側のイベント等でJavaScriptの関数等を呼び出して使えるので、画面は全てSwiftでもJavaScriptが使用できるというのが、アプリ作成の際に非常に役立ちました。
似たようなシチュエーションで開発する場合は使用してみたら良いかもしれません。
ちなみに、基本的な使用方法については本記事に記載した通りかと思うのですが、ライブラリのソースコードやREADME等を参照してみたい場合は[開発者様のGitHubアカウント](https://github.com/marcuswestin/WebViewJavascriptBridge)をご覧になってください。（ただし、全て英文です。）
また、もし何かおかしなところ等ございましたらコメントをお願いいたします。