WKWebViewのJavaScriptとNativeの連携（使用例付き）

# WKWebViewのJavaScriptとNativeの連携（使用例付き）

## ⭐️WKUserScript

### 概要
- 任意のJSからWKUserScriptを作成して、WebViewのインスタンス生成前にScript挿入処理を行うことでJSの処理をWebViewに反映できる

### 使用例
- 下記コードでは以下2つのJSをexcutableJSScript()で作成して実行しています
   - WebView内ページでメニューボタンを非表示にする
   - WebViewで拡大・縮小しないようにする

   
```swift
    override func viewDidLoad() {
        super.viewDidLoad()

        let controller = WKUserContentController()
        
        // controllerにWKUserScriptを追加
        controller.addUserScript(excutableJSScript())
        
        // WKWebView インスタンス生成時に適用
        let configuration = WKWebViewConfiguration()
        configuration.userContentController = controller

        webView = WKWebView(frame: view.frame, configuration: configuration)
        view.addSubview(webView)
    }
        
      // 任意の WKUserScriptを作成
     private func excutableJSScript() -> WKUserScript {
            
        // メニューアイコン非表示処理のscript
        let disableWebMenuIconScript = "WebView内ページのメニューアイコンを非表示にするJS"
        
        // 拡大・縮小無効処理のscript
        let disableWebViewScalingScript = "拡大・縮小無効にするJS"

       // 2つのScriptを足して1つのScriptにする
        let excutableJSScript = WKUserScript(source: disableWebMenuIconScript + disableWebViewScalingScript, injectionTime: .atDocumentEnd, forMainFrameOnly: true)

        return excutableJSScript
    }

```

[公式Doc](https://developer.apple.com/documentation/webkit/wkuserscript/)

## ⭐️evaluateJavaScript()
### 概要
- すでにインスタンス生成したWebViewに対してJSを反映する

### 使用例 
- ネイティブで取得した値をWeb側にJSで渡してあげる
- WebView内の要素を取得してアプリに表示させる

下記コードではユーザIDを取得するJSを使用して、クロージャで取得したIDを処理しています

```swift
_viewController?.webView.evaluateJavaScript("document.querySelector('#user_id').innerText", completionHandler: { userID, error in

            if let userID = userID as? String {
                Repro.set(userID: userID) // Repro（Push配信サービス）にIDを送る処理
            }
            if let error {
                print(error)
            }
        })

```

### 注意点・備考
- インスタンスがあって、WebView内の要素を取得したいときはWebViewの読み込みが完了してからじゃないとできない（以前、読み込み前に呼び出して何も取得できなくて、困った時がありました…）

```swift
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
    print("読み込み完了")
}
```

- クロージャを引数にとっているので、処理完了後に別の処理を呼び出すことも可能

```swift
func evaluateJavaScript(_ javaScriptString: String, completionHandler: ((Any?, Error?) -> Void)? = nil)
```

[公式Doc](https://developer.apple.com/documentation/webkit/wkwebview/1415017-evaluatejavascript)


## ⭐️decidePolicyFor
### 概要
- リンク先が特定外のときはブラウザで開いたりする時に使用
- WKNavigationDelegateで定義されているメソッド

### 使用例
- 下記コードでは`shouldAllowURL() `でURLがWebViewで開くべきURLだったら`true`、ブラウザで開くべきであれば`false`を返して、判断しています。

```swift
    func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
    
        if let url = navigationAction.request.url {
            if !URLAllowList.shared.shouldAllowURL(url) { // リンク先が特定外のときはブラウザで開く
                UIApplication.shared.open(url)
                decisionHandler(.cancel)
                return
            }
        }
        decisionHandler(.allow)
    }
```


[公式Doc](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455641-webview/)


## ⭐️runJavaScriptAlertPanelWithMessage
### 概要
- Web側で仕込まれたJSのアラートパネルをWebViewにも表示、ボタンアクションを実行できる
- WKUIDelegateで定義

### 使用例
```swift
func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void) {

        // 受け取ったメッセージでアラートを作成
        let alert = UIAlertController(title: nil,
                                      message: message,
                                      preferredStyle: .alert)

        let action = UIAlertAction(title: "OK", style: .default) { _ in
            // OKボタン押下時の処理
            completionHandler()
        }

        alert.addAction(action)
        present(alert, animated: true, completion: nil)
    }
```

[公式Doc](https://developer.apple.com/documentation/webkit/wkuidelegate/1537406-webview/)


## ⭐️runJavaScriptConfirmPanelWithMessage
### 概要
- Web側で仕込まれたJSのコンファームパネルを表示、ボタンアクションを実行できる
- WKUIDelegateで定義

### 使用例
```swift
    func webView(_ webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (Bool) -> Void) {
    
        // 受け取ったメッセージでアラートを作成
        let alert = UIAlertController(title: nil,
                                      message: message,
                                      preferredStyle: .alert)

        // キャンセルアクション
        let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { _ in
            completionHandler(false)
        }
        // OKアクション
        let okAction = UIAlertAction(title: "OK", style: .default) { _ in
            completionHandler(true)
        }

        alert.addAction(cancelAction)
        alert.addAction(okAction)
        present(alert, animated: true, completion: nil)
    }
```

[公式Doc](https://developer.apple.com/documentation/webkit/wkuidelegate/1536489-webview/)