■主な機能
元々の ASIHTTPRequest にある機能はもちろんご利用いただけます。多すぎて説明し切れませんので、以下の記事を参考にしていただければと思います><
http://d.hatena.ne.jp/ninjinkun/20101122/1290394265
http://macisv.jp/blog/?p=235
さらに今回私が作成した ASIAPIRequest にはこのようなおいしい特典がつきました。
- POST のパラメータだけではなく、 GET のパラメータも楽々生成してくれるメソッドを用意しました。
- 非同期実行時の通知方法が, delegate, blockに加え、さらに NSNotification による通知もサポートしました。
- 非同期的に取得したレスポンスの値をパースするためのコールバックを用意しました。このコールバックメソッドをオーバーライドして、サブクラスで処理を行えば、この中の処理はすべて非同期実行されるため、 XML のパースが遅くて UI が固まったなんてことはもうありません。
- おまけ的にタグとかつけられるようにしてみました。
■ダウンロード
github にリポジトリを作りましたので、こちらから git でクローンするか、または master のソースコードをダウンロードしてください。
https://github.com/akisute/asi-http-request
タグが付与されていますが、これはクローン元のタグなので、当てにしないでください。常に master の先端をダウンロードするのが一番確実です。
ダウンロードしたら、 Classes ディレクトリと External ディレクトリの中身を適当に自分のプロジェクトにコピーして、プロジェクトに追加していただければOKです。最後に、以下のフレームワークをリンクしてください。
- CFNetwork
- SystemConfiguration
- MobileCoreServices
- CoreGraphics
- zlib
■使い方
最初に ASIAPIRequest を継承してサブクラスを作成します。
// APIAuthorize.hでは次にAPIインスタンスを生成するためのクラスメソッドをサブクラスの内部に作ってみましょう。
@interface APIAuthorize : ASIAPIRequest {
}
// 認証APIのインスタンスを生成する
+ (id)apiWithUserId:(NSString :)userId password:(NSString :)password;
@end
// APIAuthorize.m最後にサブクラス内部でスーパークラスのメソッドをオーバーライドし、通信完了直後に呼び出される処理を記述します。たとえば、レスポンスが返ってきた際に、受け取ったレスポンスをパースして DB に保存したりします。このコールバック内部は UI スレッドとは別のスレッドで並列実行されているので、この中でどれだけ重い処理をしても UI は固まりません。その代わり UI を操作する処理はここでは行わないでください。クラッシュします。
+ (id)apiWithUserId:(NSString :)userId password:(NSString :)password {
NSURL *url = [NSURL URLWithString:@"authorize.json" relativeToURL:API_BASE_URL_STRING];
APIAuthorize *api = [APIAuthorize requestWithURL:url];
api.requestMethod = @"POST";
[api setPostValue:userId forKey:@"userId"];
[api setPostValue:password forKey:@"password"];
api.postRequestFinishedNotificationName = @"APIAuthorizeDidFinishNotification"; // POST 成功時に飛ぶnotificationの名前
api.postRequestFailedNotificationName = @"APIAuthorizeDidFailNotification"; // POST 失敗時に飛ぶnotificationの名前
return api;
}
// APIAuthorize.mtag プロパティを使って、同じAPIでレスポンスの種類を分けることができたりします。
- (void)postRequestFinished {
// レスポンスステータスコードが異常系の場合はなにもしない
if (self.responseStatusCode != 200) {
return;
}
// レスポンスをパースしてオブジェクトにし、Core Dataに保存する
// 保存したオブジェクトをuserInfoに格納しておく
User *user = [User managedObjectFromJsonString:[self responseString]
inContext:[AppDelegate appDelegate].managedObjectContext];
[[AppDelegate appDelegate].managedObjectContext save:nil];
self.userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
user, @"user",
nil];
}
- (void)postRequestFailedWithError:(NSError *)theError {
// なにもしない
}
// APIAuthorize.mこれで API 本体は完成したので、早速実行してみましょう。以下の4つの方法で実行が可能です。
enum {
APIAuthorizeResponseTagUser,
APIAuthroizeResponseTagToken,
};
+ (id)apiWithUserId:(NSString :)userId password:(NSString :)password {
NSURL *url = [NSURL URLWithString:@"authorize.json" relativeToURL:API_BASE_URL_STRING];
APIAuthorize *api = [APIAuthorize requestWithURL:url];
api.requestMethod = @"POST";
[api setPostValue:userId forKey:@"userId"];
[api setPostValue:password forKey:@"password"];
api.postRequestFinishedNotificationName = @"APIAuthorizeDidFinishNotification"; // POST 成功時に飛ぶnotificationの名前
api.postRequestFailedNotificationName = @"APIAuthorizeDidFailNotification"; // POST 失敗時に飛ぶnotificationの名前
api.tag = APIAuthorizeResponseTagUser // このAPIのレスポンスはUser型だよーとタグをつけておく
return api;
}
- (void)postRequestFinished {
switch (self.tag) {
case APIAuthorizeResponseTagUser:
// Userオブジェクトを作る
break;
case APIAuthroizeResponseTagToken:
// Tokenオブジェクトを作る
break;
default:
break;
}
}
- 同期実行
- 非同期実行、 delegate で結果を通知してもらう
- 非同期実行、 NSBlock で通信完了後の処理を行う
- 非同期実行、 NSNotification で結果を通知してもらう
// 適当に認証とかする画面のViewController.mこの通知はメインスレッドから呼び出されるので、自由に UI を操作することが可能です。
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
// Notification通知を開始する
[[NSNotificationCenter defaultCenter] addObserver:self
name:@"APIAuthorizeDidFinishNotification" // さっき決めた文字列
target:self
action:@selector(apiAuthorizeDidFinish:)
object:nil];
}
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
// Notification通知をオフにする
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (IBAction)startAuthorize {
ASIAPIRequest *api = [APIAuthorize apiWithUserId:self.userIdField.text password:self.passwordField.text];
[api startAsynchronomus];
}
- (void)apiAuthorizeDidFinish:(NSNotification *)notification {
// 認証完了時の処理
}
delegate と比べて、 NSNotification を使った通知の便利な点は以下の通り。
- delegate を使う場合には、 delegate オブジェクトがメモリから消える前に delegate の始末を行う必要があるが、 NSNotification を使った場合にはその必要がない。自分を NSNotificationCenter から削除するだけでよいので、 API リクエストを比較的投げっぱなしにできる。
- delegate とは違い、複数のオブジェクトで同時に通知を受け取ることができる。たとえば API を実行した画面とは全然違う別の画面二つで同時に通知を受け取ったりすることが可能になる。
■余談
この ASIHTTPRequest は本当にすばらしいです。最初にこの通信ライブラリを知ったときは、また良くあるただのちょっと便利なだけな通信ライブラリなんだろうと思い気にもとめなかったのですが、実際にコードを見てびっくりしました。私がほしかった通信ライブラリそのものだったからです。
私は通信ライブラリは NSOperation を継承して作るべきだと考えており、実際以前に試してみたことがありました。それは主に、
- NSOperationQueue のシングルトンインスタンスが勝手に通信をすべて管理してくれるので、クラスの変数として通信クラスのインスタンスを保持しておかなくても良くなるかもしれない
- NSOperation には依存関係を指定するメソッドがあるので、これを用いれば自動的に通信 A, B, C を順序通りに実行するなどできるかもしれない
- NSOperation を継承すれば、将来 Apple の中の人がフレームワークを改善した際にマルチコア化した iPhone の CPU の恩恵を自動的に受けられるかもしれない
- 現在全体の何%まで読み込みが完了したかを delegate で通知できる
- 通信完了時に呼び出される delegate method はすべて main thread から呼び出されるので、 UI 操作をしても安全