読者です 読者をやめる 読者になる 読者になる

PhoneGap(Cordova) 2.3.0 から使える InAppBrowser でURLのマルチバイト対応

PhoneGap(Cordova) は 2.3.0 からプラグインだった childBrowser 相当のものを本家にとりこんだ。PhoneGapとは別に WebView をたちあげたい場合に使う。
PhoneGap API Documentation
まあドキュメントのまんまで、 window.open をラップしてくる。というか勝手にラップしてしまう。URL を指定、cordova の WebView (つまりアプリ)に立ち上げるか、InAppBrowser を立ち上げるか、システムデフォルトにするか選べる。オプションとしてなんかまあ渡せる。便利。open はいいとして、 close があり、イベントリスナーとして loadstart, loadstop, exit ととれるので URL のフックもできる。

ちなみに関係ないが console.log もラップしてくれて Eclipse の LogCat や Xcode の Output に出力してくれる。便利。

問題

Android だと問題なかったのだけど、iOS だとなんだかうごかなかった。トラブルシューティングした結果、日本語を含む文字列だと動かなかった。なので、オレオレ対応した。まあ、要は URL のエスケープだ。ありがち。

トラブルシューティング

CDVInAppBrowser.m を読む。

#define    kInAppBrowserTargetSystem @"_system"

ここが使われているところが該当部分。探す。すると、条件分岐がある。

- (void)open:(CDVInvokedUrlCommand*)command
{
    CDVPluginResult* pluginResult;

    NSString* url = [command argumentAtIndex:0];
    NSString* target = [command argumentAtIndex:1 withDefault:kInAppBrowserTargetSelf];
    NSString* options = [command argumentAtIndex:2 withDefault:@"" andClass:[NSString class]];

    self.callbackId = command.callbackId;

    if (url != nil) {
        NSURL* baseUrl = [self.webView.request URL];
        NSURL* absoluteUrl = [[NSURL URLWithString:url relativeToURL:baseUrl] absoluteURL];
        if ([target isEqualToString:kInAppBrowserTargetSelf]) {
            [self openInCordovaWebView:absoluteUrl withOptions:options];
        } else if ([target isEqualToString:kInAppBrowserTargetSystem]) {
            [self openInSystem:absoluteUrl];
        } else { // _blank or anything else
            [self openInInAppBrowser:absoluteUrl withOptions:options];
        }
     ...

で、以下で実行している。

- (void)openInSystem:(NSURL*)url
{
    if ([[UIApplication sharedApplication] canOpenURL:url]) {
        [[UIApplication sharedApplication] openURL:url];
    } else { // handle any custom schemes to plugins
        [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVPluginHandleOpenURLNotification object:url]];
    }
}

最初は NSURL をどうこうできないか探したけど、なんか難しいっぽかった。独自スキームを実行してね!って書いてあるけど、そういうわけでもなさそうだった。というかObj-Cはほぼ読めない。ちょっと最近独学しようとしてたところで、まだなにもできない。

しかしどうやら NSString で先にエスケープしてしまえばいいっぽいと気がついたので、そうした。

- (void)open:(CDVInvokedUrlCommand*)command
{
    CDVPluginResult* pluginResult;

    NSString* url = [command argumentAtIndex:0];
    NSString* target = [command argumentAtIndex:1 withDefault:kInAppBrowserTargetSelf];
    NSString* options = [command argumentAtIndex:2 withDefault:@"" andClass:[NSString class]];
    // ここを追加
    // modified by atasatamatara, escape multibyte
    url = [url stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    ...

できました

まあ

ありがちな話。Objective-Cを軽くかじっていてよかった。