iOS8では,WebKitの一部がAPIとして公開された. 元々,OSXではDOMにまでアクセスでき,自由に使えていたWebKitだが,iOSでは,UIWebView程度しか利用できなかった. iOS8では,限定的にWebKitの機能がAPIを一部公開する形で利用できるようになったのである. まず,簡単な機能面から利用できるようになったものを見てみる.

できるようになったこと

  1. 履歴
  2. ステータスコード
  3. 読み込みの進捗状況の推定値
  4. Javascriptのアラートの受け取り

他にもいくつかあるが,目立ったものをリストにしてみた. OSXなら当たり前にできるのだが,UIWebViewではできなかった機能だ.

WebKitで利用するクラス

WebKitを利用するためのクラスがいくつか公開された.

ドキュメントがあるもの

  1. WKBackForwardList
  2. WKBackForwardListItem
  3. WKFrameInfo
  4. WKNavigation
  5. WKNavigationAction
  6. WKNavigationResponse
  7. WKPreferences
  8. WKProcessPool
  9. WKScriptMessage
  10. WKUserContentController
  11. WKUserScript
  12. WKWebViewConfiguration
  13. WKWebView
  14. WKWindowFeatures

ドキュメントがない,あるいはほとんどないもの

  1. WKUserScript

WKWebViewを使ってみる

WKWebViewは,UIWebViewのようにStoryboard上でコントロールとして追加できない. このため,コードでviewに配置することになる.

WKWebView *webView = [[WKWebView alloc] initWithFrame:CGRectZero configuration:nil];
webView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:webView];

webView.navigationDelegate = self;
webView.UIDelegate = self;

// Autolayout
NSDictionary *views = NSDictionaryOfVariableBindings(webView);
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[webView(>=0)]-0-|"
					options:0
					metrics:nil
					views:views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[webView(>=0)]-0-|"												  options:0
					metrics:nil
					views:views]];
[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.apple.com"]]];

この辺の使い勝手は,UIWebViewと変わらない.

JavascriptをHTMLに追加してみる

WKWebViewConfigurationオブジェクトを使って,WKWebViewを生成する. このときに,WKWebViewConfigurationオブジェクトにJavascriptを事前にセットしておくと,WKWebViewで読み込んだHTMLにそのJavascriptのコードを読み込ませることができる. まずは,JavascriptをセットしたWKWebViewを生成してみる.

// Create WKWebViewConfiguration object in order to attach javascript to html content.
WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
configuration.userContentController = [[WKUserContentController alloc] init];

// Javascript
NSString *src
    = @"function hoge() {alert('this is alert message which is defined on Objective-C.');}"

// Create javascript to attach html content.
WKUserScript *script = [[WKUserScript alloc] initWithSource:src
											  injectionTime:WKUserScriptInjectionTimeAtDocumentStart
										   forMainFrameOnly:YES];

// Add javascript to configuration object
[configuration.userContentController addUserScript:script];

// Create and add WKWebView to self.view.
WKWebView *webView = [[WKWebView alloc] initWithFrame:CGRectZero configuration:configuration];

以上である. WKWebViewConfigurationオブジェクトがWKUserContentControllerプロパティを持ち,WKUserContentControllerオブジェクトがWKUserScriptオブジェクトの追加対象となる. WKUserScriptクラスは,現在ドキュメントがないので,ヘッダファイルを見て,大体の利用方法を類推してみた. WKUserScriptオブジェクトの生成時にJavascript本体,スクリプトを挿入するタイミング,挿入するフレームをメインフレームに限定するかのフラグの3つを指定してWKUserScriptオブジェクトを生成できる.

WKWebViewに,このhogeを実行する,以下のようなHTMLを読み込ませる.

<html>
<script>
function OnButtonClick() {
    hoge();
}
</script>
<body>
    <font size=20>
        <input type="button" value="push" onclick="OnButtonClick();"/>
    </font>
</body>
</html>

ボタンを押すと,アラートが表示されるはず・・・・だが,表示されない.なぜだろう.これはWKWebViewを使う場合,Javascriptのアラートを自前で処理しなければならないためである.

WKUIDelegateプロトコル

このプロトコルは,ユーザがWKWebViewを操作したときに,対応するためのプロトコルである. たとえば,Javascriptからalert(‘hoge’);を実行した場合,webView:runJavaScriptAlertPanelWithMessage:initiatedByFrame:completionHandler;がコールされる. ゆえに,上のHTMLコンテンツのボタンを押したタイミングでアラートを表示させるためには,以下のようにコードを書く必要がある.

- (void)webView:(WKWebView *)webView
    runJavaScriptAlertPanelWithMessage:(NSString *)message
                      initiatedByFrame:(WKFrameInfo *)frame
                     completionHandler:(void (^)())completionHandler {
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@""
                                                    message:message
                                                   delegate:nil
                                          cancelButtonTitle:nil
                                          otherButtonTitles:@"OK", nil];
    [alert show];
    completionHandler();
}

他には,confirm,promptを使ったユーザインタラクションも同様に以下の二つのWKUIDelegateプロトコルを用いて実装する必要がある.

- (void)webView:(WKWebView *)webView
    runJavaScriptConfirmPanelWithMessage:(NSString *)message
                        initiatedByFrame:(WKFrameInfo *)frame
                       completionHandler:(void (^)(BOOL result))completionHandler {
    completionHandler(YES);
}

- (void)webView:(WKWebView *)webView
    runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt
                              defaultText:(NSString *)defaultText
                         initiatedByFrame:(WKFrameInfo *)frame
                        completionHandler:(void (^)(NSString *result))completionHandler {
    completionHandler(@"New value set here.");
}

WKUIDelegateには,もう一つメソッドがあり,それは,既存のWKWebView内で新しいウィンドウやフレームを指定してコンテンツが開かれようとしているときに,そのコンテンツを読み込むWKWebViewを生成して,WebKitのランタイムに渡すメソッドである.

- (WKWebView *)webView:(WKWebView *)webView
    createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration
               forNavigationAction:(WKNavigationAction *)navigationAction
                    windowFeatures:(WKWindowFeatures *)windowFeatures {
    // create new WKWebView
    WKWebView *newWebView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:configuration];
    [self.view addSubview:newWebView];
    return newWebView;
}

このサンプルは,指定されたConfigurationを使って作成したWKWebViewオブジェクトをそのまま追加するだけのコードであり,厳密には正しくないことを留意して欲しい. WKWindowFeaturesは,Javascriptのwindow.openfeaturesに対応しているようだ. HTML側でJavascriptを使って開いたウィンドウの設定をここで受け取ることができる.