PhoneGap: NativeCodeの呼び出し
PhoneGap(iPhone)を使うと、JavaScriptからGPSなどアプリ用のデバイス機能にもアクセスできるのだが、どのような仕組みになっているのか不思議だったので調査した。
JavaScript -> Native(Objective-C) Codeの呼び出し
GPSの機能などを使う場合、JavaScriptからObjective-Cで書かれたプラグインを呼び出す。
Objective-Cのコードを呼び出すには、PhoneGapLib/javascripts/core/phonegap.js.baseのPhoneGap.exec()を使う。
PhoneGap.exec()はコマンドをキューに入れるだけで、キュー内のコマンドの実行はPhoneGap.run_command()で行う。
PhoneGap.run_command()はダミーのiframeを作成して、
gap://<Class>.<command>/[<arguments>][?<dictionary>]
のような形式のURLを作成してRequestを発行する。
Requestが発行されると、Objective-Cのコードが動き出す。UIWebViewDelegateにwebView:shouldStartLoadWithRequest:navigationType:
というメソッドがあり、これは、webViewがコンテンツの読み込みを開始する前に呼び出される。これを使って、gap://へのアクセスを捕まえている。PhoneGapの場合、PhoneGapLib/Classes/PhoneGapDelegate.mの PhoneGapDelegateクラスのwebView:shouldStartLoadWithRequest:navigationType:メソッドが呼び出される。
webView:shouldStartLoadWithRequest:navigationType:メソッドは、ReuqestのURLがgap://だった場合は、URLからメソッド名や引数を抽出し、プラグインのメソッドを呼び出す。
例として、位置情報を扱うPhoneGap用のJavaScriptコードPhoneGapLib/javascripts/core/geolocation.jsでは、以下のようにしてNativeCodeを呼び出している。
JavaScript側: PhoneGap.exec("Location.startLocation", args); // gap://xxxへRequest発行 Objective-C側: webView:shouldStartLoadWithRequest:navigationType: // メソッドが呼ばれる gap://xxxxへのアクセスなら、URLからプラグインのメソッドと引数を抽出し呼び出し。 "Location.startLocation"に対応する PGLocation.startLocation(); を呼び出し。この中で、位置情報取得処理が行われる。
以上のようにして、JavaScriptコードからObjective-Cで書かれたコードを呼び出し、NativeアプリからしかアクセスできなかったGPS機能などが使えるようになっている。
うまい実装のしかただ。
Objective-CからJavaScriptへデータを渡す
一方、呼び出されたObjective-CのコードからJavaScriptへ値を返す必要もある(取得した位置情報を返すなど)。これは、UIWebViewのstringByEvaluatingJavaScriptFromStringメソッドを使えば簡単に実現できる。stringByEvaluatingJavaScriptFromStringは引数の文字列で与えたJavaScriptコードを実行する。
位置情報関連のプラグインであるPhoneGapLib/Classes/Location.mでは以下のようにしてJavaScriptのgeolocation.setLocation()を呼び出して、JavaScript側に位置情報を返している。
NSString * jsCallBack = [NSString stringWithFormat:@"navigator.geolocation.setLocation({ timestamp: %.00f, %@ });", epoch, coords]; [super writeJavascript:jsCallBack]; (writeJavaScriptメソッドはstringByEvaluatingJavaScriptFromStringを呼び出す)