iOS 11 WKWebView 3大新機能 (WWDC 2017)

# iOS 11 WKWebView (WWDC 2017)

[WWDC 2017 Customized Loading in WKWebView](https://developer.apple.com/videos/play/wwdc2017/220/) の動画より得た情報です。
現時点でiOS 11はbeta 1なので変更が加わる可能性があります。

iOS 11のWKWebViewでは3つの大きな変更が加わります。

* Cookie管理
* コンテンツブロック
* URL Scheme を用いたカスタムリソースの注入

どれもずっと欲しがっていた機能ですね。それでは見ていきましょう。

## Cookie管理

いままでのWKWebViewではCookieは非常に扱いづらいものでしたが、iOS 11 でようやくコントロール出来るようになりました。

* 各Cookieの追加・削除
* すべてのCookieへのアクセス
* HTTP-only cookie (JavaScriptからは参照できないcookie) にもアクセス可能
* cookie store変更の監視

Cookieの操作は非同期で行われ、それぞれの操作には完了時に呼ばれるClosureがあります。

```swift:WKHTTPCookieStore
let cookieStore = webView.configuration.websiteDataStore.httpCookieStore

// 追加
let cookie = HTTPCookie(properties: [ 
  HTTPCookiePropertyKey.domain: "canineschool.org",
  HTTPCookiePropertyKey.path: "/",
  HTTPCookiePropertyKey.secure: true,
  HTTPCookiePropertyKey.name: "LoginSessionID",
  HTTPCookiePropertyKey.value: "5bd9d8cabc46041579a311230539b8d1"])
cookieStore.setCookie(cookie!) {
  webView.load(aURLRequest)
}

// 取得
cookieStore.getAllCookies() { (cookies) in
  for cookie in cookies {
    // 各cookieの処理
  }
}

// 削除
cookieStore.delete(cookie!) {
  webView.load(aURLRequest)
}
```

## コンテンツブロック


Safariのコンテンツブロック拡張と同じ書式です。以下のようなことが可能になります。

* 読み込みのブロック
* コンテンツの非表示
* httpをhttpsに変更して読み込む

JSON書式のブロックリストは千、万単位でかなり大きなサイズになることがありますが、バイトコードに効率よくコンパイルされ使用されます。読み込み時のパフォーマンス低下はありません。

```swift:swift
let jsonString = """
[{
  "trigger": {
    "url-filter": ".*"
  },
  "action": {
    "type": "make-https"
  }
}]
"""
```

`"""`で囲まれた部分はSwift 4 で追加された Multi-line string literals (ヒアドキュメントのような書式)

```swift:WKContentRuleListStore
WKContentRuleListStore.default().compileContentRuleList(
  forIdentifier: "ContentBlockingRules",
  encodedContentRuleList: jsonString) { (contentRuleList, error) in
    if let error = error {
      return
    }
}
```

一度コンパイルすればデバイスに保存され、以降はIdentifierを用いて参照します。

```swift:WKContentRuleListStore
WKContentRuleListStore.default().lookUpContentRuleList(forIdentifier: "ContentBlockingRules") {
  (contentRuleList, error) in
  // 一度コンパイルしたルールリストを使用
}
```

詳細は別記事のこちら
:link: [iOS 11 WKWebViewで広告などのコンテンツブロックをする](http://qiita.com/ShingoFukuyama/items/d9b7dd86a9a4bfdc6b22)


## URL Scheme を用いたカスタムリソースの注入

端末内の写真データ等をWKWebViewに注入できる機能です。

![d1.png](https://qiita-image-store.s3.amazonaws.com/0/8920/b9305a7b-8aeb-7835-f9b0-1d0f16fc8185.png)

ゲーム内のリソースをWKWebViewを使って表現することが容易になりました。

![d2.png](https://qiita-image-store.s3.amazonaws.com/0/8920/ca9dfa90-15f4-06b5-df04-a627f9e19947.png)


動画のデモではあらかじめ指定したURL Schemeのリクエストがあった場合にフォトライブラリから写真を選んで結果をDataインスタンスに変換しmime typeなどを指定してWKWebViewに注入しています。

```html:html
...
<body>
  <img src="canineschool-avatar:///photo" class="diploma">
</body>
```

```swift:WKURLSchemeHandler
...
  func setup() {
    let configuration = WKWebViewConfiguration()
    // URL Schemeとハンドラークラスを登録
    configuration.setURLSchemeHandler(ImageSchemeHandler(), forURLScheme: "canineschool-avatar")

    let webView = WKWebView(frame: getFrame(), configuration: configuration)
    self.view.addSubview(webView)
    webView.load(URLRequest(url: URL(string: "http://example.com/demoContent")!))
  }
}

class ImageSchemeHandler : NSObject, WKURLSchemeHandler, UIImagePickerControllerDelegate, UINavigationControllerDelegate {

  var task: WKURLSchemeTask?

  ...
  
  func webView(_ webView: WKWebView, start urlSchemeTask: WKURLSchemeTask) {
    task = urlSchemeTask
    // ピッカーを表示して画像を選ぶ処理
  }
  func webView(_ webView: WKWebView, stop urlSchemeTask: WKURLSchemeTask) {
    // キャンセルされたときの処理など
    task = nil
  }

  func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
    // 画像選択完了
    picker.dismiss(animated: true)

    // データ変換＆注入
    guard let task = task else { return }
    let image = info[UIImagePickerControllerOriginalImage] as! UIImage
    let data = UIImageJPEGRepresentation(image, 1.0)!
    // response情報とdataの両方を指定
    task.didReceive(URLResponse(url: task.request.url!, 
                           mimeType: "image/jpeg",
              expectedContentLength: data.count, 
                   textEncodingName: nil))
    task.didReceive(data)
    task.didFinish()
  }
}
```

URL Scheme `"canineschool-avatar"` の部分は任意(社名やバンドルネームなど)でWebKitが扱うURL Scheme (tel, mailtoなど)とは異なるものを指定します。


---

WWDCの動画で3回にわたってデベロッパーからのフィードバックをくれと強調していました。今回実現した3つの新機能もトップ3のリクエストだったようです。欲しい機能があったらガンガンお願いしましょう。