【Swift4】WKWebViewを使ったときに調べたこと

##はじめに

WKWebViewを初めて使って調べたときに、
バラバラとした情報が多い印象で、
少々苦労した記憶があるので、備忘録として記載します。

とはいえ初心者向けです・・・！(｀・ω・´)


##[WKWebView](https://developer.apple.com/documentation/webkit/wkwebview)とは？
>　iOS 8では、これまで用いられていたブラウザエンジンであるUIWebViewに加え、「WKWebView」という新しいエンジンが追加されました。
WKWebViewはその実行速度、安定性、そして機能面でも、UIWebViewを大幅に向上しています。
要するに、WKWebViewを用いるだけで、アプリの実行速度が大幅にアップする。

引用元：[ハイブリッドアプリの動作性能を飛躍的にアップさせるiOS 8の新ブラウザエンジンを検証する](https://codezine.jp/article/detail/7848)

要するに、アプリでWebページを表示している、あれです。(｀・ω・´)

iOS8.0以上では、WKWebViewを使用するのが主流のようです。
(UIWebViewは非推奨。)

そして、WKWebViewは、storyboard上で設定できないらしい・・・！
(ソース：[WKWebViewと向き合ってみた](https://qiita.com/UJIPOID/items/fd4b33cac48ad37733f5))ので、
今回は、WKWebViewをコードで画面に配置しました。

##環境
- X-code 9.4.1
- Swift4.1

##WKWebViewの基本的なデリゲートメソッド
[WKNavigationDelegate](https://developer.apple.com/documentation/webkit/wknavigationdelegate)に関してです。
記載しているメソッド以外にも色々ありますが、(どれを使うべきか、悩みました・・・)
今回は実際に使ったことのある基本的なメソッドを、４つ記載しています。

###①Webページの読み込み開始時に、処理を実行したい
```.swift
/// リクエスト開始時に呼ばれる
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
        // ここに処理を書く！
        decisionHandler(WKNavigationActionPolicy.allow) // WKNavigationActionPolicyを書かないとクラッシュします！
}
```
- Post時に呼ばれるイベント
- そのままページを表示するか、読み込みを中止する(ページを表示しない)かを決める
- リダイレクトが起これば連続して呼ばれる
- navigationTypeを見るとクリックよる遷移か、リダイレクトによる遷移かが分かる
- ```decisionHandler(WKNavigationActionPolicy.allow)```
  →ページを表示する 
- ```decisionHandler(WKNavigationActionPolicy.cancel)```
  →ページを表示しない 

WKNavigationActionPolicyを書かないと、以下のようなエラーでクラッシュします。
地味に萎えるので、忘れないように注意しましょう！

```
Decides whether to allow or cancel a navigation.
Your delegate can either call the block immediately or save the block and call it asynchronously at a later time.
```

###②Webページが表示完了した時に、処理を実行したい

cookieの値チェックなどは、ここで行うと良さそうです。(｀・ω・´)
(このメソッドか呼ばれる前は正しいcookieの値が取得できないことがありました。)

```.swift
    /// WebView表示完了
    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        // ここに処理を書く！
    }
```

###③Webページ読み込みエラー時に、処理を実行したい

オフラインのチェック等をしたい場合に、これを使ってうまくいきました。(｀・ω・´)

```.swift
    /// WebView読み込み時にエラーが発生
    func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
        // ここに処理を書く！
    }
```

###④Webページ読み込み失敗時に、処理を実行したい
```.swift
    ///  WebView読み込み失敗
    func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
        // ここに処理を書く！
    }
```



+.:ﾟ☆ﾟ:.+.:ﾟ☆ﾟ:.+.:ﾟ☆ﾟ:.+ +.:ﾟ☆ﾟ:.+.:ﾟ☆ﾟ:.+.:ﾟ☆ﾟ:.+ +.:ﾟ☆ﾟ:.+.:ﾟ☆ﾟ:.+.:ﾟ☆ﾟ:.+ 





ちなみに、これらのメソッドは、親ViewControllerを拡張して

```.swift
extension ViewController: WKNavigationDelegate {
}
```
内で呼びます。

それと、基本的なことですが、

```.swift
import WebKit
```
を書かないと、上記のデリゲートメソッドを書いてもエラーになるので、忘れないようにしてください・・・w
参考：[WebKit](https://developer.apple.com/documentation/webkit)



##WKWebViewをAutoLayout設定してViewに配置する

画面いっぱいにWebViewを表示するだけなら
AutoLayoutはあまり意識しないでも済む(?!)かもしれませんが、
WebViewのサイズ調整、AutoLayoutが必要な場面は多々あるかなと思います。
(iPhoneXだとレイアウト調整必要ですし・・・)

今回は、フルスクリーン表示用と、
tabBarControllerに表示用に適当なメソッドを作りましたので、
その両方を、使い方と一緒に備忘録として記載しておきます。

※ちなみに、このtabBarControllerにwebViewを貼るだけをコードで書いてますw
ご了承ください。
<img width="376" alt="スクリーンショット 2018-12-15 16.10.13.png" src="https://qiita-image-store.s3.amazonaws.com/0/186535/673e62db-f188-0a64-d24a-2855fda09cee.png">


```フルスクリーンに表示.swift

    /// WebView画面(フルスクリーンに表示時)のAutoLayoutを設定
    ///
    /// - Parameters:
    ///   - webView: 表示するWebView
    ///   - view: WKWebViewを乗せるview
    public func setLayoutFullScreenWebView(webView: WKWebView, view: UIView) {

        // AutoLayout設定
        webView.translatesAutoresizingMaskIntoConstraints = false

        // webViewの幅をviewに合わせる
        view.addConstraints([NSLayoutConstraint(item:webView, // AutoLayoutを設定する対象のview(ここではWKWebViewを指定)
                                           attribute:NSLayoutAttribute.width, // AutoLayout設定対象のViewの起点を指定
                                           relatedBy:NSLayoutRelation.equal, // 2つのview(ここでは親viewとWKWebView)の拘束関係を示すもの
                                              toItem:view, // 拘束の基準となるview
                                           attribute:NSLayoutAttribute.width, // 設定値はattributeと内容は同じ
                                          multiplier:1.0, // 親viewに対して何パーセントの大きさにするかを設定(例:70%なら0.7)
                                            constant:0)]) // 位置に加えるオフセット (例:親のビューの中心を基準にそろえるものの、それに対して常に30ポイントだけ正方向にずらしたい場合は30を設定する)
        // webViewの高さをviewに合わせる
        view.addConstraints([NSLayoutConstraint(item:webView,
                                                attribute:NSLayoutAttribute.height,
                                                relatedBy:NSLayoutRelation.equal,
                                                toItem:view,
                                                attribute:NSLayoutAttribute.height,
                                                multiplier:1.0,
                                                constant:0)])
    }
```

```tabBarControllerに表示.swift
    /// WebView画面(tabbarControllerに表示時)のAutoLayoutを設定
    ///
    /// - Parameters:
    ///   - webView: 表示するWebView
    ///   - view: WebViewを乗せるview
    public func setLayoutWebView(webView: WKWebView, view: UIView) {
        // AutoLayout設定
        webView.translatesAutoresizingMaskIntoConstraints = false
        // webViewの上端をviewに合わせる
        view.addConstraints([NSLayoutConstraint(item:webView,
                                                attribute:NSLayoutAttribute.top,
                                                relatedBy:NSLayoutRelation.equal,
                                                toItem:view,
                                                attribute:NSLayoutAttribute.top,
                                                multiplier:1.0,
                                                constant:0)])
        // webViewの幅をviewに合わせる
        view.addConstraints([NSLayoutConstraint(item:webView,
                                                attribute:NSLayoutAttribute.width,
                                                relatedBy:NSLayoutRelation.equal,
                                                toItem:view,
                                                attribute:NSLayoutAttribute.width,
                                                multiplier:1.0,
                                                constant:0)])
        // webViewの下端をviewに合わせる
        view.addConstraints([NSLayoutConstraint(item:webView,
                                                attribute:NSLayoutAttribute.bottom,
                                                relatedBy:NSLayoutRelation.equal,
                                                toItem:view,
                                                attribute:NSLayoutAttribute.bottom,
                                                multiplier:1.0,
                                                constant: -49.0)]) // 画面下のtabbarを考慮
    }
```


・[translatesAutoresizingMaskIntoConstraints](https://developer.apple.com/documentation/uikit/uiview/1622572-translatesautoresizingmaskintoco)
コードでAutoLayoutを設定する時にはtranslatesAutoresizingMaskIntoConstraintsをオフにする必要があるみたい(デフォルトがONで思った通りのAutoLayoutが設定できない場合があるそう)なので、オフにします。

・[NSLayoutConstraint](https://developer.apple.com/documentation/uikit/nslayoutconstraint)
レイアウトはNSLayoutConstraintを使用しています。
(参考にした記事は、いちばん下の参考一覧に記載しています。)


##Webページを表示するリクエスト

実際にURLのWebページを表示するリクエストです！
これも、複数のViewControllerから呼び出せるように適当なメソッドを作っています。

```.swift
    /// リクエスト
    ///
    /// - Parameters:
    ///   - urlString: 表示するURL
    ///   - webView: 表示先のwebView
    public func requestUrl(urlString: String, webView: WKWebView) {
        // URL設定
        let encodedUrlString = urlString.addingPercentEncoding(withAllowedCharacters: NSCharacterSet.urlQueryAllowed)
        // Webページを読み込む
        let url = NSURL(string: encodedUrlString!)
        let request = NSURLRequest(url: url! as URL)
        webView.load(request as URLRequest)
    }
```
おおまかな手順は下記です。(｀・ω・´)

#####①[URLエンコード](https://wa3.i-3-i.info/word13354.html)

- [addingPercentEncoding(withAllowedCharacters:)]
(https://developer.apple.com/documentation/foundation/nsstring/1411946-addingpercentencoding)を使用して、URLで使えない文字を別の文字に置き換えます。

#####②URLを[NSURL](https://developer.apple.com/documentation/foundation/nsurl)に変換
#####③[NSURLRequest](https://developer.apple.com/documentation/foundation/nsurlrequest)でリクエストを生成
#####④webViewにURLのWebページを表示するようにリクエスト

この辺に関しては、WKWebViewではありませんが[swift：UIWebViewでwebページを引っ張ろう！！](http://programming-beginner-memo.com/?p=25)の記事がわかりやすかったです。


##実際にWebページを表示してみる
```呼び出し元.swift

    private var webView: WKWebView = WKWebView()

    // 表示するURL
    private let urlString: String = "https://www.yahoo.co.jp/"
    
    override func viewDidLoad() {
        super.viewDidLoad()

        // WKWebViewの追加
        webView = WKWebView(frame:CGRect(x: 0, y: 0, width: view.bounds.size.width, height: view.bounds.size.height))
        view.addSubview(webView)
        
        // 画面いっぱいに表示
        self.setLayoutFullScreenWebView(webView: webView, view: view)

        webView.navigationDelegate = self

        self.requestUrl(urlString: urlString, webView: self.webView)
    }
```

![Simulator Screen Shot - iPhone 8 Plus - 2018-12-15 at 15.38.03.png](https://qiita-image-store.s3.amazonaws.com/0/186535/a7f506f2-5db4-2d28-e6ab-ea449e7f6529.png)








＼(^o^)／





簡単にwebViewを表示することができましたw

##まとめ
勉強し始めの頃は、自分の好きなサイトをwebViewに表示するだけでも、
表示できたときに嬉しいですし、地味にテンション&モチベーションもあがるので
試しにWKWebViewを使ってみるのは、個人的に初心者の方におすすめできます＼(^o^)／


##参考・WKWebView関連記事
- [WKWebViewのリダイレクト時の挙動](http://takuyaokamoto.hateblo.jp/entry/2017/07/31/033835)
- [UIWebViewを使わない理由とWKWebViewを使う理由](https://qiita.com/ShingoFukuyama/items/01a9cdf24090931b4ef3)
- [【iOS8】 WKWebView内の画面遷移をフックする](https://ponpoko1968.hatenablog.com/entry/controlling-navigation-wkwebview)
- [Auto Layoutをコードから使おう](https://blog.personal-factory.com/2016/01/11/make-auto-layout-via-code/)
- [iOSにおけるHTTPリクエストの投げ方](https://github.com/mixi-inc/iOSTraining/blob/master/Swift/pages/day3/4-2_HTTP-request-basic.md
)
-[WKWebViewに舐めてかかって返り討ちにあったアレコレ](https://qiita.com/aryzae/items/9b3d6f77cb5082665220)
-[iOS 11 WKWebView 3大新機能 (WWDC 2017)](https://qiita.com/ShingoFukuyama/items/c2d33ff3fc36dbdb69a4)
-[WKWebViewの新しい機能を使ってみました](https://recruit.gmo.jp/engineer/jisedai/blog/ios11_wkwebview/)