Ti.UI.WebView内のHTMLからオブジェクトを受けとったりTitaniumのメソッドを呼ぶ

/ Text by

TitaniumでTi.UI.WebViewで外部サイトを開いて、外部サイト内で何らかの処理を行い、その結果をTitanium側にオブジェクトを受け渡したりあるいはメソッドを呼びたい場合がある。この場合、Titaniumに

Ti.App.addEventListener("custom_event", callback);

としてカスタムイベントを用意し、WebViewで開く外部サイトのJavaScriptで

Ti.App.fireEvent("custom_event", { "foo": "bar" });

とすれば良い。

が、これはWebView内で開いた外部サイトでページ遷移を行わない場合。ページ遷移が発生した場合、遷移後のページではTi.App.fireEvent()が機能しなくなりこの方法ではお手上げになる。ただしこれはSDK 1.8.0.1のときの仕様なので最新バージョンでは改善されているかも知れない。

諸々試行錯誤して導きだした最も確実な方法は、あまり美しくないですがTitanium側でtimerを使ってWebViewを監視する。やっていることは単純で、Titanium側で一定間隔でevalJSして外部サイトからオブジェクトが返ってくるのを待つというもの。なんだかとっても泥臭いですが目的はこれで果たせるはず。timerの止め忘れには注意。


外部サイトで行う処理

var custom_event;

// 外部サイト側で必要な処理を全て終えたときにcustom_eventにオブジェクトを返す関数を放り込む。
// このオブジェクトの内容をTitaniumで受け取れることになる。
custom_event = function () {
    return '{"foo": "bar"}';
}

Titaniumで行う処理

/* ============================================== */
/* Window内にWebViewを作成 */

var webview = Ti.UI.createWebView({ url: 'http://domain.com/' });
var window = Ti.UI.createWindow();
window.add(webview);
window.open();


/* ============================================== */
/* Timerの処理 */

var mytimer;

function timerFunc () {
    webView.removeEventListener('load', timerFunc);

    mytimer = setInterval(function () {
        var obj = JSON.parse(webView.evalJS('if (custom_event) { custom_event(); }'));
   
        if (obj) {
            if (mytimer) {
                // 処理が終わり不要になったtimerを停止・削除する
                clearInterval(mytimer);
                mytimer = null;
            }
       
            callback(obj);
        }
    }, 500);
}


/* ============================================== */
/* callbackの処理 */

function callback (obj) {
    // callback時の処理を書く
}


/* ============================================== */
/* WebView内で外部サイトのロードが完了しだいtimerを起動させる */

webView.addEventListener('load', timerFunc);