WKWebViewでのSessionの共有

# 概要

WkWebViewを使ったWebViewアプリでSession情報を共有するための方法をメモ

# WKWebView間でSessionの共有

`WKWebView` 間でSessionを共有するためには `WKProcessPool` を使います
要注意なのはこの `WKProcessPool` のインスタンスの共有の仕方で、 **必ず `WKProcessPool` を設定した `WKWebViewConfiguration` のインスタンスを `WKWebView` の初期化の際に渡すようにする必要があります**
`WKWebView` を初期化した後に、 `WKWebViewConfiguration` のsetter経由で設定してもSessionは共有されないようです

◯ Sessionが共有される

```swift
let configuration = WKWebViewConfiguration()
let processPool = WKProcessPool()
configuration.processPool = processPool
let webView = WKWebView(frame: .zero, configuration: configuration)
```

×Sessionが共有されない

```swift
let configuration = WKWebViewConfiguration()
let processPool = WKProcessPool()
let webView = WKWebView(frame: .zero, configuration: configuration)
webView.configuration.processPool = processPool
```

`WKProcessPool` のインスタンスはアプリケーション内で共有する必要があるため、staticにして保持することをおすすめします。

# 次回起動時もSessionを共有する

`HTTPCookieStrage` と `WKProcessPool` のセッション情報は非同期で共有されてしまうため、
同期的にSession情報を扱うためには、

1. 初回リクエスト時のHTTP Response HeaderのSet-Cookieの値を保存する
2. 次回起動時にSession情報を引き継ぐ

をする必要があります

### 1.  初回リクエスト時のHTTP Response HeaderのSet-Cookieの値を保存する

Set-Cookieで渡されるCookie情報を保存するには、[webView(_:decidePolicyFor:decisionHandler:)](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455643-webview) の `navigationResponse`  にリクエストしたサーバーからCookieが渡された時に `UserDefault` 等に保存すればよいかと思います

```swift
func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
    if let urlResponse = navigationResponse.response as? HTTPURLResponse,
      let url = urlResponse.url,
      let allHeaderFields = urlResponse.allHeaderFields as? [String : String] {
      let cookies = HTTPCookie.cookies(withResponseHeaderFields: allHeaderFields, for: url)
      for cookie in cookies {
        if cookie.domain == "リクエストしたサーバーのドメイン"  {
          // UserDefaultにCookie情報を保存する
        }
      }
    }
    decisionHandler(.allow)
  }
```

###  2.次回起動時にSession情報を引き継ぐ

アプリケーションを終了し、次回起動時にSession情報を引き継いでリクエストするためには、

a. `WkWebView` の初期化時に `document.cookie` に `_session_id` を設定する
b. `WkWebVie` の初回リクエスト時にCookie情報を設定する

をする必要があります

#### a. `WkWebView` の初期化時に `document.cookie` に `_session_id` を設定する

`WKUserScript ` 経由で `document.cookie` に `_session_id` を設定することで、初回リクエスト時以降もSession情報を保つことができます

```swift
let userContentController = WKUserContentController()
let script = "document.cookie='_session_id=セッションID; domain=サーバードメイン; path=Cookieパス;"
let cookieScript = WKUserScript(source: script, injectionTime: WKUserScriptInjectionTime.atDocumentStart, forMainFrameOnly: false)
userContentController.addUserScript(cookieScript)

let configuration = WKWebViewConfiguration()
configuration.userContentController = userContentController
```

セッションID、サーバードメイン、Cookieパスについては、[webView(_:decidePolicyFor:decisionHandler:)]の `navigationResponse` で渡される [HTTPCookie](https://developer.apple.com/documentation/foundation/httpcookie) の値を確認してみてください

#### b. `WkWebVie` の初回リクエスト時にCookie情報を設定する

初回リクエスト時にSession情報を設定してリクエストするためには、[load](https://developer.apple.com/documentation/webkit/wkwebview/1414954-load) メソッドに渡す `URLRequest` のインスタンスにCookie情報を設定する必要があります
注意点として、 `httpShouldHandleCookies` を falseに設定してリクエストする必要があります
trueにした場合、 `HTTPCookieStrage` の値を共有してしまい、正しくSession情報が渡せない可能性があります

```swift
var request = URLRequest(url: url)
request.httpShouldHandleCookies = false
request.setValue("_session_id=セッションID", forHTTPHeaderField: "Cookie")
webview.load(request)
```

# 参考


- [ios - Cookie sharing between multiple WKWebViews - Stack Overflow](https://stackoverflow.com/questions/25797972/cookie-sharing-between-multiple-wkwebviews)
- [ログイン認証したあとに、WKWebViewでCookieを使ってセッションを保つ方法と失敗例](https://qiita.com/yonell/items/bbd45c64a0068f7dc9c3)
- [ガワネイティブのCookie同期](http://takuyaokamoto.hateblo.jp/entry/2017/07/03/013705)