Javascriptを使ってiOSのWebViewからネイティブにデータを返そう

Swift使っていますか?弊社ではまだほとんど使っていません。今日もObjective-Cの話題です。

iOSアプリを作るとき、すでにWeb版で実装されている機能をiOSでもそのまま使いたいがためにUIWebViewを使うということがあると思います(あると言ったらある)。そしてアプリケーションが複雑になってくるとそのUIWebViewとデータのやりとりをしたくなってくることがあるわけです。iOSからWebViewにデータを渡すのは比較的簡単で、シンプルな場合はURLにパラメータをつけてGETリクエストを投げてあげれば目的は達成できます。

逆にWebViewからデータをiOS側に戻すときが少々ややこしくなります。WebViewからiOSにPUSH的にデータを渡すことはほぼできないと思われるので、このケースではiOS側からデータを取りにいかなければいけません。

幸い、WebViewには読み込みが始まったときや終わったときなどを検知するためのコールバックが用意されています。例えば読み込み完了時にWebViewのデータを読み取ってあげればよいわけです。

- (void)webViewDidFinishLoad:(UIWebView *)webView
{
    NSString *body = [webView stringByEvaluatingJavaScriptFromString:@"document.body.innerHTML"];
    NSLog(@"html body: %@", body);
}

上のようにWebViewのJavascriptを呼べるので、document経由でHTMLのドキュメントを取り出していくことが可能です。というか、Javascriptを呼べるということはそもそも自由度が高いわけで結構なんでも出来てしまいますね。

JSON文字列を返す

WebViewで何か処理した結果をJSONとしてiOS側で使いたいというケースがあるとしましょう。そういうときはHTMLに<script>タグでJavascriptの関数を定義してあげます。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <title>Untitled Document</title>
    </head>
    
    <body>
        <script type="text/javascript">
            function getJson() {
                var json = {
                    "name": "CFlat",
                    "address": {
                        "prefecture":"Shizuoka",
                        "city":"Hamamatsu"
                    }
                };
                return JSON.stringify(json);
            }
        </script>
    </body>
</html>

あとは先ほど使ったものと同じようにstringByEvaluatingJavaScriptFromStringを使ってこの関数を呼び出せばOK。

- (void)webViewDidFinishLoad:(UIWebView *)webView
{
    NSString *jsonString = [webView stringByEvaluatingJavaScriptFromString:@"getJson()"];
    NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
    NSDictionary *json = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingAllowFragments error:nil];
    NSLog(@"json: %@", json);

}

出力されるログはこうなりました。

2014-10-07 13:13:52.674 MyApp[20993:1596809] json: {
    address =     {
        city = Hamamatsu;
        prefecture = Shizuoka;
    };
    name = CFlat;
}

WebViewで表示する側も自分たちで担当できるのであればこの方法は自由度が高くて便利ではないでしょうか。既存のWeb資産の活用するのにも役立ちそうですね。