Intelligent Technology's Technical Blog

株式会社インテリジェントテクノロジーの技術情報ブログです。

Cordova-iOS 4.0 を試す

こんにちは、野口です。

2015年12月にWKWebViewに対応したCordova-iOS 4.0.0が正式にリリースされました。(2016年1月現在、最新はCordova-iOS 4.0.1になっています。)
今回は実際にCordovaでWKWebViewを動かしてみたいと思います。

github.com

実行環境

実行環境は以下の通りです。

ホストPC OS X EL Capitan 10.11.3
Xcode 7.2
cordova 5.4.1
iOS 9.2.1 / 8.3

プロジェクトの作成

    # プロジェクト作成
    # cordova create <<作成先フォルダ名>> <<アプリの識別子>> <<プロジェクト名>>
    $ cordova create sample jp.co.iti.wkwebview WKWebViewSample
    $ cd sample/

    # iOS 用プロジェクト生成
    $ cordova platform add ios@latest
    Adding ios project...
    iOS project created with cordova-ios@4.0.1
    Discovered plugin "cordova-plugin-whitelist" in config.xml. Installing to the project
    Fetching plugin "cordova-plugin-whitelist@1" via npm
    Installing "cordova-plugin-whitelist" for ios

    # 正しく追加さている確認
    $ cordova platform ls
    Installed platforms: ios 4.0.1
    Available platforms: amazon-fireos, android, blackberry10, browser, firefoxos, osx, webos

CordovaでWKWebViewを利用するにはプラグインを追加する必要があるため、プラグインを追加します。
なお、Cordova上でWKWebViewが使用できるのはiOS9のみで、iOS8で動かした場合
はUIWebViewが使われます。
github.com

    # WKWebView を利用するためのプラグインを追加
    $ cordova plugin add cordova-plugin-wkwebview-engine --save
    Fetching plugin "cordova-plugin-wkwebview-engine" via npm
    Installing "cordova-plugin-wkwebview-engine" for ios
    Saved plugin info for "cordova-plugin-wkwebview-engine" to config.xml

このプラグインを追加すると、以下のファイルが追加されます。

f:id:IntelligentTechnology:20160126153838p:plain


実際にアプリを起動し、どのWebViewが使用されているかを確認します。

iOS9で確認すると、WKWebViewが使用されています。
f:id:IntelligentTechnology:20160126143652p:plain

iOS8で確認すると、UIWebViewが使用されています。
f:id:IntelligentTechnology:20160126144650p:plain

ただ、iOS8で確認した際、Variable ViewにはengineWebViewはUIWebViewとありますが、コンソールを確認すると以下のようなログが出力されてます。

2016-01-26 14:53:55.875 WKWebViewSample[5047:253914] Using WKWebView
2016-01-26 14:53:55.955 WKWebViewSample[5047:253914] Using UIWebView

これは本当にUIWebViewが使われているのか気になるので、Cordovaの実装を見て確認してみます。

- (UIView*)newCordovaViewWithFrame:(CGRect)bounds
{
    NSString* defaultWebViewEngineClass = @"CDVUIWebViewEngine";
    NSString* webViewEngineClass = [self.settings cordovaSettingForKey:@"CordovaWebViewEngine"];

    if (!webViewEngineClass) {
        webViewEngineClass = defaultWebViewEngineClass;
    }

    // Find webViewEngine
    if (NSClassFromString(webViewEngineClass)) {
        self.webViewEngine = [[NSClassFromString(webViewEngineClass) alloc] initWithFrame:bounds];
        // if a webView engine returns nil (not supported by the current iOS version) or doesn't conform to the protocol, or can't load the request, we use UIWebView
        if (!self.webViewEngine || ![self.webViewEngine conformsToProtocol:@protocol(CDVWebViewEngineProtocol)] || ![self.webViewEngine canLoadRequest:[NSURLRequest requestWithURL:self.appUrl]]) {
            self.webViewEngine = [[NSClassFromString(defaultWebViewEngineClass) alloc] initWithFrame:bounds];
        }
    } else {
        self.webViewEngine = [[NSClassFromString(defaultWebViewEngineClass) alloc] initWithFrame:bounds];
    }

    if ([self.webViewEngine isKindOfClass:[CDVPlugin class]]) {
        [self registerPlugin:(CDVPlugin*)self.webViewEngine withClassName:webViewEngineClass];
    }

    return self.webViewEngine.engineWebView;
}

ここでは、使用するwebViewEngineを設定しています。CDVWKWebViewEngineもしくはCDVUIWebViewEngineが設定されます。名前の通り、WKWebViewを使用する場合はCDVWKWebViewEngine、UIWebViewを使用する場合はCDVUIWebViewEngineが使用されます。

webViewEngineにまずCDVWKWebViewEngineが設定されますが、その後にWKWebViewが使用できないか判定し、使用できない場合、webViewEngineにCDVUIWebViewEngineが設定されます。
そのため、「Using WKWebView」というログが出た後に、「Using UIWebView」というログが出力されていました。

制約

Cordovaの問題ではなく、WKWebViewの問題になりますが、制約があります。
cordova-plugin-wkwebview-engineのREADMEを確認すると、以下の文言があります。

> you are still not able to use XHR from the file:// protocol without CORS
enabled on your server.

実際に、iOS9でAJAXリクエストを試してみましたが、以下の結果のように上手くいきませんでした。

①ローカルファイル(file://)から、同じく、ローカルファイルへのAJAXリクエスト
    var req = new XMLHttpRequest();
    req.open("GET", "sub/local.json", true);
    req.setRequestHeader('Cache-Control','no-cache');
    req.send(null);

結果:以下のエラーが出て、リクエスト失敗しました。
「Failed to load resource:Origin null is not allowed by Access-Control-Allow-Origin.」

f:id:IntelligentTechnology:20160127181342p:plain

また、「req.setRequestHeader('Cache-Control','no-cache');」なしで、リクエストを投げたところ、「Cross origin requests are only supported for HTTP.」というエラーになりました。

f:id:IntelligentTechnology:20160127181314p:plain

②ローカルファイル(file://)から、WebサーバーへのAJAXリクエスト

サーバーサイドで「Access-Control-Allow-Origin: *」と設定済み
結果:以下のエラーが出て、リクエスト失敗しました。Access-Control-Allow-Originの設定をしていてもダメなようです。
「Failed to load resource:Origin null is not allowed by Access-Control-Allow-Origin.」


f:id:IntelligentTechnology:20160127181418p:plain
f:id:IntelligentTechnology:20160127181451p:plain

上記のリクエストヘッダ・レスポンスヘッダはOPTIONメソッドで送信されているのでプリフライトリクエストのものと思われます。そこでステータスコード200が返ってきていますが、リソースの読み込みに失敗しています。

iOS8でプラグイン呼び出しに失敗する

cordova-plugin-wkwebview-engineを入れているときに、iOS8でプラグイン呼び出しを行うと、以下のエラーが出て、プラグインを使用することができませんでした。

「TypeError: undefined is not an object (evaluating'window.webkit.messageHandlers')」

Cordova + WKWebView ではNative-WebViewのブリッジの方法がUIWebViewを使用するときとは異なっていました。
しかし、cordova-plugin-wkwebview-engineを入れていると、UIWebViewでもそのWKWebView用の新しい方法でプラグイン呼び出しを行おうとして、失敗していました。

この不具合は既にCordovaのJIRAに登録されているので、次回のアップデートで修正されると思われます。

まとめ

Cordova-iOS 4.0.1 でWKWebViewを試してみましたが、まだ制約や不具合があり、使用するには注意が必要そうです。