UIWebViewに甘んじてた俺がWKWebViewというパンドラの箱を開けた話

#話の前に
[CYBIRDエンジニア Advent Calendar 2015](http://qiita.com/advent-calendar/2015/cybird?share=true) 20日目を担当する新卒入社3年目の@sgtです。

19日目は@cy-katsuhiro-miuraさんの「[2016年になっても、これさえ入れておけば大丈夫。Jenkins Plugin Must Install 16選!!](http://qiita.com/cy-katsuhiro-miura/items/513e97a44a5b21cbf76c)」でした。
新卒の時からお世話になっている尊敬する先輩@cy-katsuhiro-miuraさん。彼の愛らしいボディと人懐っこい性格はうちの社員を虜にしています。(ねこの紹介みたいになっちゃった…)

#はじめに
iOS8から使えるようになったWKWebView。
しかしそれはパンドラの箱であり、UIWebViewで大体のことできんじゃんという考えから敬遠されがちだったのではないだろうか。
しかし遂に我々はパンドラの箱を開けるときがやってきた…！

なぜ開けないければならなくなったか。

そう、iOS9の出現だ。
どうもiOS9だと戻ると更新の挙動がうまく働かないらしい。
なんでもbackbone.jsを使用することによりシングルページで管理するので、History APIを使用して履歴を残しているのだがiOS9ではそれがうまく効かないとか。


はぁ……とうとう重い腰をあげるときが来たか。

よしそうと決まればUIWebViewでの実装とWKWebViewでの実装にどんな変化が起きたか見ていこうか。

#変化を読み解く
さてまずは、初期化からか。
まぁこれはそんなに大差ないな。

```objc:UIWebView
-(void)init {
  UIWebView *webView = [[UIWebView alloc]init];   
  webView.delegate   = self;
}
```
```objc:WKWebView
#import <WebKit/WebKit.h>

...

-(void)init {
  WKWebView *webView         = [[WKWebView alloc]init];    
  webView.navigationDelegate = self;
  webView.UIDelegate         = self;
}
```
ほ…ほぅ、デリゲートの設定が2つになったのか。

デリゲート使う際にエラー出とるな…そうかそうかヘッダーファイルに必要な記述あったもんな。

```objc:UIWebView
@interface SgtViewController : UIViewController<UIWebViewDelegate>
{
```
```objc:WKWebView
@interface SgtViewController : UIViewController<WKNavigationDelegate, WKUIDelegate>
{
```
まぁまぁまぁこれはシンプル。
うん、ここはわかる。

エラーは至る所に出ているがまずは問題の「戻る」と「更新」をWKWebView仕様にしよう。

```objc:UIWebView
if([webView canGoBack]) {
    [webView goBack];
}
```
```objc:WKWebView
if([webView canGoBack]) {
    [webView goBack];
}
```
…お分かりいただけただろうか。
何もメソッド名が変わっていないことを。
ちょっと意外だったわー、でもありがたいわー。

ってことは更新も？

```objc:UIWebView
[webView reload];
```
```objc:WKWebView
[webView reload];
```

YES!!!!!
いやぁ楽勝だったわーwww

一旦他コメントアウトしてビルド通してみよ。

うん、うまくいったwww

まぁとはいえコメントアウトしたままでは既存のアプリの機能からランクダウンしてしまうからな。
他の変更箇所なんかも紹介しようと思うよ。

#更なる高みへ
まずはObjective-CからJavaScriptを実行する場合ね。
いきなりレベル上がった感するねー。
高み登ってるねー。

```objc:UIWebView
[webview stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"%@.setItem('%@', '%@');", @"localStorage", @"platform", @"apple"]];
```
```objc:WKWebView
[webview evaluateJavaScript:[NSString stringWithFormat:@"%@.setItem('%@', '%@');", @"localStorage", @"platform", @"apple"]
          completionHandler:^(NSString *result, NSError *error){
              NSLog(@"result:%@", result);
              if(error) {
                  NSLog(@"error:%@", [error description]);
              }
}];
```

お次はURLスキームの設定ですねー。
調べてみたらこれは少ーしWKWebViewの方がめんどくさいかも。
とりあえずApp Storeに飛ばすやりかたね。

```objc:UIWebView
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
    NSURL *requestURL = [request URL];
    if([[requestURL path] rangeOfString:@"//itunes.apple.com/"].location != NSNotFound){
        [[UIApplication sharedApplication] openURL:requestURL];
        return;
    }
    // 後は好きなの書いちゃって
}
```
```objc:WKWebView
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
    NSURL    *url       = navigationAction.request.URL;
    NSString *urlString = [url absoluteString];
    if([urlString rangeOfString:@"//itunes.apple.com/"].location != NSNotFound){
        [[UIApplication sharedApplication] openURL:url];
        decisionHandler(WKNavigationActionPolicyCancel);
        return;
    }
    // 後は好きなの書いちゃって
}
```
調べてみるとUIWebViewとWKWebViewでwebページを読み込む際に呼ばれるデリゲートメソッドが結構ちがうなぁ。
では余談として、どういう順番で呼ばれるのかUIWebViewとWKWebViewを呼ばれる順番に比較しながら見ていこー！
まずはwebページを読み込みを開始する前に呼ばれる↓

```objc:UIWebView
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;
```
```objc:WKWebView
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation;
```
お次は、読み込みを開始したとき↓

```objc:UIWebView
- (void)webViewDidStartLoad:(UIWebView *)webView;
```
```objc:WKWebView
- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation;
```
最後は読み込みが完了したとき↓

```objc:UIWebView
- (void)webViewDidFinishLoad:(UIWebView *)webView;
```
```objc:WKWebView
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation
```
ちなみに読み込みに失敗したときは↓

```objc:UIWebView
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error;
```
```objc:WKWebView
- (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error
```
ここを変えてやらないとエラーが出まくるから気をつけよう！

#UIWebViewユーザが未体験の世界へ

ここからはUIWebViewにはなかった機能をちょっと見ていこう。

まずは簡単なところから。
なんとWKWebViewでは表示中のページのURLやタイトルが取得できるようになったらしいのだ。

```objc:WKWebView
// 表示中のページのURL
NSURL    *pageURL   = webview.URL;
// 表示中のページのタイトル
NSString *pageTitle = webview.title;
```
地味にゴイスー。

お次はページの閲覧履歴を取得できるというもの。

```objc:WKWebView
// 閲覧履歴をarrayに格納
NSArray *backForwardListArray = [[webView.backForwardList.backList reverseObjectEnumerator] allObjects];
```
などなどWKWebViewではUIWebViewではできなかった様々なことができるようになってると。

#最後に
発表から1年以上たっているWKWebkit。
だが、まだまだ十分な情報が世に出回っているとはいえない状況である。
その中で大変参考になった記事がある。
今回僕は実体験に基づいて触りをお話したわけだが、更に詳しく知りたい方は是非参考にしていただければと思う。
[WKWebViewで躓いた10つのまとめ](http://qiita.com/ShingoFukuyama/items/eede79a284c3669846e9)
[WKWebViewとUIWebView](http://www.slideshare.net/yukihirai52/wkwebviewanduiwebview)

明日は@segashoさんの「サーバー運用大変。そうだ。MSPに委託しよう」です。
今までやっていなかったことへの取り組みは楽しみですね。