【Swift】WKWebViewでJavaScriptを実行する方法

# はじめに
WKWebViewでJaveScriptを実行する方法を二つ紹介します。

# とりあえずWKWebViewを表示
以下がベースのコードです。
こちらに追加していきます。
```swift
import UIKit
import WebKit

class ViewController: UIViewController {

    var webView: WKWebView!

    override func viewDidLoad() {
        super.viewDidLoad()

        webView = WKWebView(frame: view.frame)

        view.addSubview(webView)

        webView.load(URLRequest(url: URL(string: "")!))

    }

}
```

# 方法1(読み込み開始時 or 読み込み終了時)
こちらの方法は読み込み開始時もしくは、読み込み終了時に実行されます。
使用用途としては、WebViewの設定や静的サイトの要素ブロックなどが考えられます。
HTMLの読み込み後にSwift.orgのヘッダーを非表示にしてみます
```diff_swift
import UIKit
import WebKit

class ViewController: UIViewController {

    var webView: WKWebView!

    override func viewDidLoad() {
        super.viewDidLoad()

+       let hiddenNavigationBarScriptString = "document.getElementsByTagName('nav')[0].style.display='none';"
+       
+       let hiddenNavigationBarScript = WKUserScript(source: hiddenNavigationBarScriptString, injectionTime: .atDocumentEnd, forMainFrameOnly: true)
+       
+       let userContentController = WKUserContentController()
+       
+       userContentController.addUserScript(hiddenNavigationBarScript)
+       
+       let configuration = WKWebViewConfiguration()
+       
+       configuration.userContentController = userContentController
+       
+       webView = WKWebView(frame: view.frame, configuration: configuration)

-       webView = WKWebView(frame: view.frame)

        view.addSubview(webView)

        webView.load(URLRequest(url: URL(string: "https://www.swift.org/")!))

    }

}
```
|通常|JS適用後|
|:-:|:-:|
|![simulator_screenshot_4FED424F-1BFC-4E00-8A1A-DD582C45E9F6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/2284b199-5fb7-ed81-b6dd-1d8c3a91fbaf.png)|![simulator_screenshot_284B6FDF-EA49-459A-826D-4CFA849AFA5B.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/c6178a33-782e-73a1-0ea4-10ce6867c655.png)|

:::note info
**ポイント**
`WKUserScript(source:, injectionTime:, forMainFrameOnly:)`

- source: JavaScriptのコードをStringで渡す
- injectionTime: 実行タイミング
    - `.atDocumentStart` ← 読み込み開始時
    - `.atDocumentEnd` ← 読み込み終了時
- forMainFrameOnly: メインフレームのみに反映するならtrue
:::

# 方法2(任意のタイミング)
こちらの方法は任意のタイミングで実行可能です。
基本的になんでもできると思います。
動的サイトであるQiitaのユーザーページでプロフィールを非表示にしてみます
```diff_swift
import UIKit
import WebKit

class ViewController: UIViewController {

    var webView: WKWebView!

    override func viewDidLoad() {
        super.viewDidLoad()

        webView = WKWebView(frame: view.frame)

        view.addSubview(webView)

        webView.load(URLRequest(url: URL(string: "https://qiita.com/SNQ-2001")!))

+       DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
+       
+           let hiddenProfileScript: String = "document.getElementsByClassName('css-t3s93c')[0].style.display='none';"
+
+           self.webView.evaluateJavaScript(hiddenProfileScript) { _, _ in
+
+           }
+           
+       }

    }

}
```

|通常|JS適用後|
|:-:|:-:|
|![simulator_screenshot_6B7B4ADD-AA68-48B6-893F-F37EE49D5509.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/51d008b5-a178-2c49-84bf-31689a2a3005.png)|![simulator_screenshot_C744C3C3-08B6-4F9E-8A33-8D8E61ADA052.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/f3776d44-7a8e-3d0f-82cf-701de7d255d4.png)|


:::note info
**ポイント1**
動的サイトはHTMLが生成されてから実行しないとスクリプトが動作しないので遅延実行させる
`DispatchQueue.main.asyncAfter(deadline: .now() + 1)`
:::

:::note info
**ポイント2**
必要な場合はresponseから値を受け取る
```swift
self.webView.evaluateJavaScript("document.body.innerHTML") { response, error in
    print(response ?? "")
}
```
:::


# 参考サイト
https://zenn.dev/yorifuji/articles/ios-wkwebview-synchronus-bridge

https://jonathanblog2000.blogspot.com/2016/11/understanding-ios-wkwebview.html