このところは、PhoneGapというHTML+JavascriptでiPhoneアプリを作れるものにハマっています。

HTMLでGUIを組み上げていくわけですが、
画像などを自分で用意し、CSSや<img>タグを駆使すれば、
リッチなGUIが簡単に完成します。

Javascriptで実装されている機能はほぼ完璧に使えますので、
煩わしいObjective-Cのソースを書くという作業は、相当数少なくなります。

速度はどうかなと思ったんですが、

SafariはPCの世界でも評判の速度。
そのパワーを借りるわけですから、
心配するほど遅いことはありません。

驚くべきライブラリです。
この前の記事にも書いていますが、
MITライセンスってとこがまたいいですよね!

、、、とまぁここまで散々褒めちぎっていたわけですが、
たとえばファイルの入出力などは、Javascriptの範疇じゃないので、
Objective-Cの力を借りたくなるのです。

さてどうしたものか、、、調べたところ、
PhoneGapのJavascriptAPIを拡張する方法が分かりましたので、書いておきます。
ある程度Objective-CとJavascriptの知識が必要ですので、
そのあたりは心してごらんください。

PhoneGapでは、JavascriptAPIを実行したとき、
gap.js(PhoneGapのJavascriptAPIファイル)が、

「gap:」を文頭につけたURLがリクエストされたものとしてWEBViewオブジェクトへリクエストし、
WEBViewは、受け取ったURLの頭が「gap:」だったら、
通常のURLリクエストとは別の動作(ここがAPIの実働部分)をするように構築されているようです。


PhoneGapにデフォルトで実装されている関数「Device.soundPlay()」を例に話をすすめましょう。

gap.jsを開くと、
Device.soundPlay関数の部分を見ると、

Device.exec()関数というのが実行されているのが分かります。
この関数が、WEBViewへのリクエストを出してくれる関数です。
そしてこの関数への引数は、「gap:sound:鳴らしたい音のファイル名」となっています。
これがWEBviewへ渡すリクエストです。

このリクエストがどのObjective-Cのメソッドで受け取られるかというと、
「GlassAppDelegate.m」の中の、
「- (BOOL)webView:(UIWebView *)theWebView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType」というメソッドです。
このメソッドの中で、渡されたURLを「:」で分割し、配列「parts」へ格納されます。

NSArray * parts = [urlString componentsSeparatedByString:@":"];

▲この部分ですね。

このpartsのインデックス0が「gap」なら通常のHTTPリクエスト以外の処理が行われるわけです。

//LOCATION
if([(NSString *)[parts objectAtIndex:1] isEqualToString:@"getloc"]){

▲この部分以降は、partsのインデックス1の値によって、機能を切り替えるわけです。

soundPlay()関数の場合、「gap:sound:ファイル名」というURLが渡されるので、
partsのインデックス1が「sound」だったときに、指定した音を鳴らす機能を実装してやっているわけです。

else if ([(NSString *)[parts objectAtIndex:1] isEqualToString:@"sound"]) {

// Split the Sound file
NSString *ef = (NSString *)[parts objectAtIndex:2];
NSArray *soundFile = [ef componentsSeparatedByString:@"."];

NSString *file = (NSString *)[soundFile objectAtIndex:0];
NSString *ext = (NSString *)[soundFile objectAtIndex:1];
// Some TODO’s here
// Test to see if the file/ext is IN the bundle
// Cleanup any memory that may not be caught
sound = [[Sound alloc] initWithContentsOfFile:[mainBundle pathForResource:file ofType:ext]];
[sound play];
}

▲それがここですね。
まず、ここで勘のいい人は気付いていると思いますが、
「gap:呼び出したい機能:疑似的な引数1:疑似的な引数2:疑似的な引数3…」
とリクエストするURLの2番目の「:」以降は、
「:」区切りで疑似的な引数を与えることができるのです。
soundPlayの場合は、最初の疑似的な引数へ鳴らしたい音ファイルのファイル名を渡しています。
上記のソースでは、このファイルを「Audio Queue Services」へ登録して鳴らしているわけです。
(Audio Queue Servicesの説明は、cocoaプログラムの作成方法をごらんください)

ざっと説明するとこんな感じです。はいめっちゃ分かりづらい!!笑)
しかし、これくらい読めないといいアプリは作れない!ってことで、
そういう意見はスルーします!

さて、さっきのsoundPlayのAPI、
Objective-Cのソース内では、この機能が呼び出される度に、
「Audio Queue Services」へファイルが登録され、
そしてそのファイルが再生されて終わり。。。という仕様になっています。
これはこれでいいのですが、なんか効率悪いですよね。。。

ってことで次回は、

音ファイルの登録と再生を別のJavascriptAPI関数として実装してみます。
お楽しみに。