2017.10.02

WKWebViewの新しい機能を使ってみました

こんにちは。GMOインターネットの次世代システム研究室のJ.Lです。
iOS 11が発表され、すでに2週間ほど経ちました。
皆さんはiOS 11の新しい機能は使ってますでしょうか。
今回はWKWebViewの新しい機能について共有したいと思います。

ちなみに、皆さんはWKWebViewを使ってますか。もし、まだUIWebViewを使ってる方がいらっしゃいましたら、ぜひWKWebViewを検討してみてください。UIWebViewがdeprecatedされることもあり、UIWebViewでは実現が難しかった(もしくはできなかった)機能(ex. ローディングの進捗率)が簡単に実現できるなど様々な便利な機能がOSバージョンアップのたびに追加されてます。
今回のiOS 11でもWKWebViewは以下の機能が追加されました。

  1. Cookieの管理
  2. コンテンツフィルタリング
  3. URL schemeによるカスタムリソース対応

では、詳しく見てみましょう。

 

Cookieの管理

WKHTTPCookieStore」を利用すことで、特定のCookieを生成または参照、削除することができます。この機能はアプリから特定Webページを表示する際に、通常のブラウザから表示する時と異なる挙動を(事前認証など)したいケースなどで利用できると思います。

以下の例はTrelloサービスの認証トークンを取得しWKWebViewに適用することで、ログイン状態になるのかを確認するSampleです。

まずは、Trelloサービスにログインし、ブラウザでCookieを確認してみましょう。私の環境では以下のように表示されました。(Chromeの画面です。)

「token」という項目が確認できます。その項目の値をコピーし以下のように実装してみましょう。

ViewController.swift

import UIKit
import WebKit

class ViewController: UIViewController {
    var webView: WKWebView!

    override func viewDidLoad() {
        super.viewDidLoad()
        self.createWebView()
    }

    func createWebView() {
        webView = WKWebView(frame: self.view.bounds)
        self.view.addSubview(self.webView)
        let cookie = HTTPCookie(properties: [
            HTTPCookiePropertyKey.domain: "trello.com",
            HTTPCookiePropertyKey.path: "/",
            HTTPCookiePropertyKey.secure: true,
            HTTPCookiePropertyKey.name: "token",
            HTTPCookiePropertyKey.value: "xxxxxx"
            ])
        let cookieStore = webView.configuration.websiteDataStore.httpCookieStore
        cookieStore.setCookie(cookie!) {
            self.webView.load(URLRequest(url: URL(string: "https://trello.com")!))
        }
    }
}

「HTTPCookiePropertyKey.value」の値を上でコピーした内容で設定し、端末で実行してみましょう。
いかがでしょうか。認証されログイン後のページが表示されましたか。

ちなみに、Cookieの値を取得したい場合はWKHTTPCookieStoreの「getAllCookies」メソッドを利用してください。そして、削除の場合は「delete」メソッドを参照してください。
具体的な使い方については簡単ですのでここでは省略します。

 

コンテンツフィルタリング

WKContentRuleListStoreを利用することでWebページをフィルタリングすることができます。もちろんアプリ側でWKWebViewのデリゲートなどで頑張れば似たような機能を実現するのは可能ですが、簡単にそして正確な動作が実現できるためより便利になったと思います。フィルタリングを設定するためにはこのページに書かれているルールをJSON形式で作成し、「compileContentRultList」メソッドでCompileする必要があります。そして、JSONには「trigger」と「action」のペアを一つ以上作成する必要があります。triggerとactionの具体的な内容についてはAppleのページ(上のリンク)に詳しく記載されてますので、そちらを参照してください。

以下の例はYahooモバイルページにてイメージダウンロード先である「yimg.jp」をブロックする例です。

ViewController.swift

import UIKit
import WebKit

class ViewController: UIViewController {
    var webView: WKWebView!

    override func viewDidLoad() {
        super.viewDidLoad()

        let ruleListJsonStr = """
        [{
            "trigger": {
                "url-filter": "yimg.jp"
            },
            "action": {
                "type": "block"
            }
        }]
        """

        WKContentRuleListStore.default().compileContentRuleList(forIdentifier: "myContentRule",
                                                                encodedContentRuleList: ruleListJsonStr) { (contentRuleList, error) in
                                                                    if let error = error {
                                                                        print("Error!=>\(error)")
                                                                        return
                                                                    }
                                                                    self.createWebViewWithRuleList(contentRuleList)
        }
    }

    func createWebViewWithRuleList(_ ruleList: WKContentRuleList?) {
        let configuration = WKWebViewConfiguration()
        if let ruleList = ruleList {
            configuration.userContentController.add(ruleList)
        }
        webView = WKWebView(frame: self.view.bounds, configuration: configuration)
        webView.uiDelegate = self
        webView.navigationDelegate = self
        self.view.addSubview(self.webView)
        self.webView.load(URLRequest(url: URL(string: "https://m.yahoo.co.jp")!))
    }
}

実行するとイメージが表示されないことを確認できます。

URL Schemeによるカスタムリソース対応

独自のURL Schemeをページに設定し、アプリで特定の動作をさせることは以前から可能でしたが、より明確にそして、確実に実現することが可能になりました。例え、ページローディング中、端末の写真ラリブラリから写真を持ってきてページに表示させるもしくは現在表示中の画面を利用してウェブページを表示するなどWebページと端末のネイティヴ機能をスムーズに融合させることができます。

以下の例はローカルの画像をimgタグに設定する例です。

sample.html

<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
  </head>
  <body>
    <h1>Hello, World!</h1>
    <img src="gmo-custom-scheme://" style="max-width: 100%"/>
  </body>
</html>

ViewContorller.swift

import UIKit
import WebKit

class ViewController: UIViewController, WKURLSchemeHandler {
    var webView: WKWebView!

    override func viewDidLoad() {
        super.viewDidLoad()
        self.createWebView()
    }

    func createWebView() {
        let configuration = WKWebViewConfiguration()
        configuration.setURLSchemeHandler(self, forURLScheme: "gmo-custom-scheme")
        webView = WKWebView(frame: view.frame, configuration: configuration)
        webView.uiDelegate = self
        webView.navigationDelegate = self
        view.addSubview(webView)
        let filePath = Bundle.main.path(forResource: "sample", ofType: "html")!
        let url = URL(fileURLWithPath: filePath)
        webView.loadFileURL(url, allowingReadAccessTo: url)
    }

    func webView(_ webView: WKWebView, start urlSchemeTask: WKURLSchemeTask) {
        let imagePath = Bundle.main.path(forResource: "logo", ofType: "png")!
        let resourceData = try! Data(contentsOf: URL(fileURLWithPath: imagePath))
        let response = URLResponse(
            url: urlSchemeTask.request.url!,
            mimeType: "image/png",
            expectedContentLength: resourceData.count,
            textEncodingName: nil
        )
        urlSchemeTask.didReceive(response)
        urlSchemeTask.didReceive(resourceData)
        urlSchemeTask.didFinish()
    }

    func webView(_ webView: WKWebView, stop urlSchemeTask: WKURLSchemeTask) {
        urlSchemeTask.didFinish()
    }
}

※「logo.png」は適当な画像をプロジェクトに追加してください。

ポイントになる部分は「webView(_:start:)」です。こちら「WKURLSchemeHandler」protocolのメソッドであり、sample.htmlの「img」タグのsrc Propertyに設定されている「gmo-custom-scheme」を解決するため、システムから呼び出されます。その際に、response(例ではイメージデータ)を作成し、システムに返すことでイメージデータを設定することができます。実際に端末で実行してみると「logo.png」がレンダリングされることが確認できます。

まとめ

iOS 11にてWKWebViewに追加された機能を簡単に紹介しました。アプリ上でWebページをレンダリングする際により便利かつ明確に実装できるような機能が追加されています。今まで実装にかなり面倒な処理を加えなければならなかった機能や実装が難しかった機能が段々と増えてますので、WKWebViewをぜひ使ってみてはいかがでしょうか。

次世代システム研究室では、アプリケーション開発や設計を行うアーキテクトを募集しています。アプリケーション開発者の方、次世代システム研究室にご興味を持って頂ける方がいらっしゃいましたら、ぜひ募集職種一覧からご応募をお願いします。