- (2011/12/19 追記)
- Google Data APIs Objective-C Client Library のコードで不要な autorelease をしていたのを修正しました。
もりやまです。先日初めて仕事として iPhone アプリの開発に携わりました。
iPhone アプリの開発が解禁された直後に少し齧って、今回ほぼ 2 年ぶりに触ってみたんですが、相変わらず ObjC には慣れないですね・・・
さて今回は、そんな ObjC での開発で、iPhone アプリ向けに使えるライブラリを調べてみました。
Google Data APIs Objective-C Client Library
gdata-objectivec-client – Project Hosting on Google Code
Google の各種サービスへアクセスするためのライブラリです。
今回の開発では、GDataXMLNode.(h|m) だけをピックアップして使ってみました。
所謂 DOM タイプのパーサです。XPath も使えます。
iOS SDK では NSXMLParser という SAX タイプのパーサが使えますが、DOM に慣れているとどうにも使いにくいです。
もちろん、メモリ消費の観点から言えば SAX の方がいいのですが、パース対象の XML の大きさが予想できて、メモリ使用量に問題が無ければ DOM の方が簡単です。
使い方は以下の通りです。
この XML を
<?xml version="1.0" encoding="UTF-8"?>
<items>
<item id="1">ほげほげ</item>
<item id="2">ふがふが</item>
</items>
このコードに通すと
NSError *error;
// 1. XML をパース
GDataXMLDocument *doc = [[GDataXMLDocument alloc] initWithData:xmlString
options:0
error:&error];
if (error != nil) { NSLog(@"%@", [[[NSString alloc] initWithData:xml encoding:NSUTF8StringEncoding] autorelease]); }
// 2. XPath で該当のノードを抽出
NSArray *nodes = [doc nodesForXPath:@"/items/item" error:nil];
for (GDataXMLElement *node in nodes) {
// 3. 抽出したノードから ID 属性とテキストノードを抽出してログに出力
NSLog(@"id = %d, name = %@", [[[node attributeForName:@"id"] stringValue] intValue], [node stringValue]);
}
[doc release];
こう出てきます。
id = 1, name = ほげほげ
id = 2, name = ふがふが
API のまとまったドキュメントが見当たらないので、ソースを読むなりググるなりするしかないのですが、基本的な機能は上記のコードで対応できると思います。
UIDevice extension
ars’s uidevice-extension at master – GitHub
UIDevice を拡張するいくつかのカテゴリです。
デバイスのステータスやケイパビリティを取得するメソッドが追加されます。
上記のオリジナルのリポジトリは 2009 年から更新されていないので、フォークして更新されているものを使います。
先程確認したら、以下のリポジトリのものが Retina ディスプレイの判別が加わっていてよさそうでした。
solydzajs’s uidevice-extension at master – GitHub
ただし、1 点だけ修正が必要になります。
UIDevice+Reachability.(h|m) で、非公開の API を使用しているコードを有効にするかどうかをマクロで切り替えられるようにしているようなのですが、判別部分が #ifdef になっているため、#define でどんな値が設定されていても、マクロが定義されているだけで必ず有効になってしまいます。
フォーク元のこのコミットで #if を #ifdef に修正したようですが、ちょっと意図がわかりませんでした。
これに気づかなくて一度リジェクトされてしまいました。。。
判別部分を #if にして、#define で定義する値を 0 にするか、#ifdef のままで、#define 自体を削ってしまう必要があります。
#define でわざわざ 1 を設定しているところからすると、#ifdef を #if に戻すのが正しいのではないかと思います。
さて、肝心の使い方は以下の通りです。
以下は UIDevice+Reachability を使ったサンプルです。
// -[UIDevice (BOOL)networkAvailable] が追加されたメソッド
if ([[UIDevice currentDevice] networkAvailable] == NO) {
alert = [[UIAlertView alloc] initWithTitle:nil
message:@"ネットワークに接続されていません"
delegate:self
cancelButtonTitle:@"OK"
otherButtonTitles:nil];
[alert show];
[alert release];
}
ネットワークに繋がっているかどうかを判別するためよく使われている Reachability.(h|m) の使い方がどうにもしっくりこなかったので、
もっとスマートにできるものはないかなぁと思ってこのライブラリを探してきたので、上のコードはその代替になるコードになります。
UIDevice オブジェクトが、ネットワークに繋がっているかどうかを知っている、というのはとても分かりやすいです。
ASIHTTPRequest
ASIHTTPRequest Documentation – All-Seeing Interactive
こちらは現在進行中のプロジェクトで使用してみました。
HTTP 通信 +αのライブラリです。
今回使用した目的は Amazon S3 のクライアントとしての機能です。
ストレージとして Amazon S3 を使うアプリを開発中なのですが、スクラッチからクライアントを作るのも面倒だし、さすがに誰か作ってるだろうということで探してみたところ、これを見つけました。
使い方は以下のような感じです。
まずバケットの一覧を取得してみます。
ASIS3ServiceRequest *request = [ASIS3ServiceRequest serviceRequest];
request.accessKey = @"S3_ACCESS_KEY";
request.secretAccessKey = @"S3_SECRET_ACCESS_KEY";
[request startSynchronous];
if (![request error]) {
NSLog(@"%@", [request.buckets retain]);
} else {
NSLog(@"%@", [request error]);
}
続いてバケットからオブジェクトの一覧を取得してみます。
ASIS3BucketRequest *request = [ASIS3BucketRequest requestWithBucket:@"hoge"];
request.accessKey = @"S3_ACCESS_KEY";
request.secretAccessKey = @"S3_SECRET_ACCESS_KEY";
request.delimiter = @"/";
request.prefix = @"";
[request startSynchronous];
NSMutableArray *directories;
NSMutableArray *objects;
if (![request error]) {
directories = [[NSMutableArray alloc] init];
objects = [[NSMutableArray alloc] init];
NSRange range = NSMakeRange(0, [path length]);
for (NSString *directory in request.commonPrefixes) {
[directories addObject:[directory stringByReplacingCharactersInRange:range withString:@""]];
}
for (ASIS3BucketObject *object in request.objects) {
if ([object.key length] > 9 && [[object.key substringFromIndex:([object.key length] - 9)] isEqualToString:@"_$folder$"]) { continue; }
object.key = [object.key stringByReplacingCharactersInRange:range withString:@""];
[objects addObject:object];
}
NSLog(@"%@", directories);
NSLog(@"%@", objects);
} else {
NSLog(@"%@", [request error]);
directories = [[NSArray alloc] init];
objects = [[NSArray alloc] init];
}
このような感じで、手探りしながら大体 2 時間くらいで S3 のバケットブラウザを作ることができました。
ドキュメントもサイトにきちんと揃っているので、とても使いやすいです。
今回紹介したものを使ったサンプルプロジェクトを、GitHub で公開していますので、よければ参考にしてください。
実際にプロジェクトで使ってみたり、デモを作ってみたりするまでできていないライブラリが他にもいくつかあるので、また試してみたら紹介したいと思います。
次回は、弊社で作った Good Choice や Wall Calendar などのコードから再利用できそうなものをまとめてフレームワーク化したので、そのフレームワークの紹介と、iOS 向けのフレームワークの作り方を紹介したいと思います。
このエントリーに対するコメント
-
有益な情報、ありがとうございます。
大変参考にさせていただいております。一点、お聞きしたいのですが、
UIDevice+Reachability.m内にて、gethostnameという
部分が必ずWarningになってしまいます。
(定義されていない?)私はプログラム自体あまり詳しく
ないので、よく分からないのですが、、
もしよろしければご回答いただけますと幸いです。どうぞ宜しくお願いいたします。
2011年04月23日, 4:13 PM
-
どのような Warning が出るのでしょうか?
Xcode 4.0.2 + iOS SDK 4.3 の環境で、記事で紹介した solydzajs リポジトリの master と、より新しい erica リポジトリの master の、それぞれに含まれている HelloWorld をビルドしてみましたが、こちらでは Warning は出ませんでした。
これらの HelloWorld でも Warning が出るでしょうか?2011年04月25日, 9:06 PM
-
ご回答ありがとうございます!
当方、Xcode 3.2.5 + iOS SDK 4.2 の環境ですので、その辺が原因でしょうか。。
ご指摘いただいたHelloWorldでも同様のWarningが出ます。
Implicit declaration of function ‘gethostname’
というような内容です。
とりあえずXcodeなど最新のものにして様子を見てみます!2011年05月05日, 6:42 PM
-
なるほど。HelloWorld でも Warning が出るということは、コードが原因ではなさそうですね。
今手元には同様のバージョンの環境がないので確認できないのですが、もしアップデートで解決したようでしたら教えて頂けると幸いです。
よろしくお願いいたします。2011年05月06日, 5:20 PM
-
Xcode 3.2.6 + iOS SDK 4.3 に更新したところ、無事Warningが
出なくなりました!
ですが、アプリ申請の時に、非公開APIを使用してます的なことでzipをアップロードしたタイミングでシステムリジェクトをくらってしまいました。。(上記の#ifdefなどの対応はしたのですが)
結局、UIDevice+Reachabilityを今回は外して申請したところ、無事Waiting for Review に入りました。
・・もう少し色々勉強してがんばります!^^;2011年05月07日, 3:39 PM
-
拝見いたしました。
読んでいて不思議に思ったことがあるので質問します。Google Data APIs Objective-C Client Library のサンプルコードで GDataXMLDocumentのインスタンスを作るときにautoreleaseしていますが、最後でreleaseも行っています。
これは何か意図がありますか?2011年12月18日, 6:30 PM
-
コメントありがとうございます。
たしかに、一方は不要ですね。
サンプルプロジェクトとして公開してある分については autorelease をしていませんでしたので、記事の方も同様に autorelease をしないように修正いたしました。
ご指摘ありがとうございました。
今後ともよろしくお願いいたします。2011年12月19日, 12:07 PM
-
#undef 〜
するのが定石じゃないかしら、、
#define の値はその機能に対する意味ありにして、未定義のときはその機能すら実装しない場合に配慮する定石だと思いますよ。2013年01月04日, 1:09 PM
日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)
- トラックバック
「いいね!」で応援よろしくお願いします!