WKWebViewでリダイレクト中のCookieを扱う

## WKWebViewでCookieを扱うことについて
例えば、アプリ内のWebViewからWebページにアクセスした際にログイン状態を維持したい時にはWebView通信時にCookieを乗せることで可能になりますが、UIWebViewからWKWebViewに乗り換えた際にリダイレクトにSet-Cookieがつく場合、次のRequestにCookieが乗らず、正常に通信できない問題があったので、色々試行錯誤した結果を書きました。

### リダイレクトにSet-Cookie有（問題のケース）
リダイレクトにSet-Cookieがある場合、次のリクエストにCookieは乗りません。

1. 302(リダイレクト(Set-Cookie=Hoge1)
**302にSet-Cookieされたものは次のリクエストには乗らない**
2. Next Request(Cookie="")

### リダイレクトにSet-Cookie無（大丈夫なケース）
リダイレクトにSet-Cookieがない場合には次のリクエストにCookieが乗ります。

1. 302(リダイレクト)
2. 200(Set-Cookie=Hoge1)
3. Next Request(Cookie=Hoge1)

## 対処
諸々調査検証した結果、WKWebViewのリダイレクト中のSet-Cookieについて、以下のことがわかってきました。

1. WKWebViewでリダイレクト中のSet-Cookieを正しく処理することはできない
2. 逆にWebviewで開いているページからの遷移では問題が起きない。

そこで`2の部分に注目し、Webにhtmlを配置し、そこを経由する`ことでリダイレクトにSet-Cookieがついても正常に通信できることがわかりました。

### redirect.html
こんなのをWebに配置します。

```javascript
<script>
	var el = document.createElement('a');
	var url = decodeURIComponent(window.location.search.substring(1));
	el.href = url;
	el.click();
</script>
```

### 使い方
アプリからは以下のように`redirect.html`のパラメータに遷移先のURLを指定して使います。

```Swift
let destUrlStr = "https://hogehoge.com"

var allowedCharacterSet = CharacterSet.alphanumerics
allowedCharacterSet.insert(charactersIn: "-._~")
guard let encodedURLString = destUrlStr.addingPercentEncoding(withAllowedCharacters: allowedCharacterSet) else {
    return nil
}

let request = URLRequest(url: URL(string: "https://example.com/redirect.html?" + encodedURLString))
webView.load(request)
```

### 色々試して駄目だったケース
リダイレクトが発生したら次のリクエストにCookieを挿入できればいいのですが、WKWebViewはその手段が用意されていなかったので、なかなか手こずりました。

#### 1. リダイレクトのイベントを捕捉して、次のリクエストにCookieを乗せる  
このメソッドはリダイレクトが発生するたびに呼ばれますが、このタイミングではまだResponseを取得できませんでした。

```swift
func webView(_ webView: WKWebView, didReceiveServerRedirectForProvisionalNavigation navigation: WKNavigation!) {
	// リダイレクトが発生するたびに呼ばれるが、このタイミングではResponseは取得できない
	print("didReceiveServerRedirectForProvisionalNavigation = \(webView.url)")
}
```    

#### 2. Request直前のイベントを捕捉して、送信する前にCookieを挿入する
このメソッドはリダイレクト含むRequest前に呼ばれるため、以下のようにリダイレクト契機であれば、リクエストし直してCookieを挿入することはできそうですが、リダイレクト契機か判定する方法がないため駄目でした。

```swift
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
	print("navigationAction Request = \(navigationAction.request)")
	if リダイレクト契機か？ {
		self.loadURL()
		decisionHandler(.cancel)
	} else {
		decisionHandler(.allow)
	}
}
```

#### 3. WKWebView.loadRequestをハックする
こちらの[記事を参考](https://novemberfive.co/blog/WKWebView-redirect-with-cookies)にWKWebView.loadRequestをoverrideして内部でURLSessionを使い、自前で制御してしまう方法ですが、この場合にはWKWebViewDelegateの各種メソッドが呼ばれないため使用しませんでした。

## サンプルコード
色々挙動を確認できるように`UIWebView/WKWebView`の[サンプルアプリ](https://github.com/ogasawara-atsushi/DemoWebView)を用意しました。
確認する際には`UIWebViewController, WKWebViewController`のurlを設定ください。

## 使用したツール
今回、検証する際に[Charles](https://www.charlesproxy.com/)を使用しました。
このツールは通信のモニタリングやRequest/Responseの編集ができたりと多岐にわたってかゆいところに手が届くため非常に使いやすかったです。

## 参考にした記事
[WKWEBVIEW REDIRECT WITH COOKIES](https://novemberfive.co/blog/WKWebView-redirect-with-cookies)
[WKWebView. My experience](https://medium.com/@Ilia_Glushchuk/wkwebview-my-experience-fe3c9849be50)
[WKWebViewのコード](https://github.com/WebKit/webkit/tree/master/Source/WebKit/UIProcess)