酢ろぐ!

カレーが嫌いなスマートフォンアプリプログラマのブログ。

iOSでUIWebViewからアプリへ文字列を渡す方法とアプリからUIWebViewへ文字列を渡す方法

本記事は「UIWebViewからアプリへ文字列を渡す方法とアプリからUIWebViewへ文字列を渡す方法 - iOSアプリ開発の逆引き辞典」に転記しました。


以前、Windows PhoneのWebBroswerコントロールで表示させたHTMLとアプリケーションとの連携にさせる方法をご紹介させて頂きました。

今回は、これらの方法をiOSで実現させる必要が出てきました。実現したい事としては、HTML(UIWebView)からアプリケーションへ通知をおこない、それに応じてアプリケーションからHTML(UIWebView)のfunctionを実行させたいと考えています。

f:id:ch3cooh393:20130208112606j:plain

アプリケーションからHTMLへ何らかのアクションをおこなう場合には、以下のメソッドを利用することが可能です。

- (NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;

Windows Phoneの場合には、HTMLからアプリケーションへは、「window.external.notify(string)」を利用して通知をおこないましたが、iOSではこのAPIがサポートされておらず、別の手段を用いる必要があります。

UIWebViewには、リンクをクリックしてページ遷移をおこなう直前に「本当にページ遷移をおこなってよいのかどうか?」を検証することが可能なデリゲートが用意されています。

- (BOOL)webView:(UIWebView *)webView
     shouldStartLoadWithRequest:(NSURLRequest *)request
                 navigationType:(UIWebViewNavigationType)navigationType;

iOSにはアプリケーションへ直接文字列を渡す方法が用意されていませんが、わざとページ遷移を発生させ、クエリ文字列にアプリケーションへ渡したい文字列を追加し、遷移先のURLをshouldStartLoadWithRequest:navigationTypeメソッドで受け取り、ページ遷移を拒否します。

HTML側

HTMLでは2つのJavaScriptのfunctionを定義しています。

notifyメソッドは、ch3coohスキームで始まる「ch3cooh://notify?msg=sakusan」へ遷移しようとします。[通知をおこなう]というリンクをクリックすると、実行されます。

replaceHtmlメソッドは、アプリケーションから呼び出されるのを期待しているメソッドで、渡された文字列をdivタグを書き換えるだけのものです。

<html>
    <head>
        <script type="text/javascript">
            function notify(msg) {
              result.innerHTML = '応答待ち';
              document.location = 'ch3cooh://notify?msg=' + msg;
            }
            function replaceHtml(parm1) {
              result.innerHTML = parm1;
            }
        </script>
    </head>
    <body>
    <a href="javascript:void(0);" onclick="notify('sakusan');">通知をおこなう</a>
    <div id="result">
        None Data.
    </div>
    </body>
</html>

アプリ側

まずはUIWebViewに先ほど作成したテストページを表示させます。

- (void)viewDidAppear:(BOOL)animated
{
    // テストページを読み込む
    NSURL* url = [NSURL URLWithString:@"https://dl.dropbox.com/u/2258039/send/sample/ios_notify.html"];
    [self.contentView loadRequest:[NSURLRequest requestWithURL:url]];
}

[通知をおこなう]というリンクをクリックすると、document.locationを変更するのでページ遷移が発生します。

// ページ遷移が発生したら実行される
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request
    navigationType:(UIWebViewNavigationType)navigationType
{
    // ch3coohスキーマ以外は通常にページ遷移をさせる
    NSString* scheme = [[request URL] scheme];
    if (![scheme isEqualToString:@"ch3cooh"]) {
        return YES;
    }
    
    // クエリ文字列をパースする
    NSString* query = [[request URL] query];
    NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
    for (NSString *param in [query componentsSeparatedByString:@"&"]) {
        NSArray *elts = [param componentsSeparatedByString:@"="];
        if([elts count] < 2) continue;
        [params setObject:[elts objectAtIndex:1] forKey:[elts objectAtIndex:0]];
    }
        
    // アプリから送信されたメッセージを受信する
    NSString* message = [params valueForKey:@"msg"];
    NSLog(@"%@", message);
        
    // 遅延実行させる
    [self performSelector:@selector(notificationHtmlFunction:)
               withObject:message afterDelay:10];
    
    // ch3coohスキーマの場合はページ遷移させない
    return NO;
}

1秒待ってからnotificationHtmlFunctionメソッドが実行されます。実行されるまでの間、「応答待ち」を表示させていたので下図のように表示されます。

f:id:ch3cooh393:20130208133530p:plain

notificationHtmlFunctionメソッドでは、シンプルに既存のJavaScriptのメソッド呼び出しをおこなうだけです。

// HTML側で定義されているfunctionを引数付きで実行する
- (void)notificationHtmlFunction:(NSString*)message
{
    NSString* msg = [NSString stringWithFormat:@"received:%@", message];
    NSString* script = [NSString stringWithFormat:@"replaceHtml('%@');", msg];
    [self.contentView stringByEvaluatingJavaScriptFromString:script];
}

上記のコードが実行されると、HTMLの文字列を変更します。下図のように表示されます。

f:id:ch3cooh393:20130208133518p:plain

以上で、iOSでのアプリケーションからUIWebView(HTML)へ文字列を送る方法と、UIWebView(HTML)から送られてきた文字列をアプリケーション側で受け取る方法をご紹介させていただきました。

ソースコード

このエントリにて紹介したサンプルプロジェクトはGitHubにて公開しております。