yo_waka's blog

418 I'm a teapot

Objective-CでHTTPリクエスト扱うライブラリ作った

sendAsynchronouseRequest個別に書くのはしんどいし、最初は簡単にラップしてBlocksでコールバック渡すのがいいかなーと思ったけどself渡すのにいちいち__weakつけて作るのがめんどくさくなった。
setTimeoutの関数に「var that = self」やるのがめんどくさいあんな感じ。

多分ほぼAPIとのやりとりになるiPhoneアプリのHTTP通信はシンプルでよくて、HTTPリクエストをセットできて個別にコールバックを書けてHTTPレスポンスを受け取れればいい。
あと最近はRESTなAPIも多いのでPUT/DELETEリクエストも使いたいところ。

JS脳なのでこんな感じで書きたいw

$.get(url, params, this.handleSuccess_, this.handleFailure);
$.getJSON(url, params, this.handleSuccess_, this.handleFailure);

NSURLRequestはパラメータをセットするのがとてもめんどくさいのでそこはラップするとして。

コールバックをBlocks以外でやるとデリゲートが真っ先に浮かぶわけですが、そうすると1対1になっちゃう。
なんかいいのないかなーと探したらNSInvocationてのがありました。
これを使うとあるオブジェクトにセレクタを引数渡して実行することができる。

こんな感じ。

// Invocationを作って
NSInvocation *invocation = nil;
NSMethodSignature *sig = [delegate methodSignatureForSelector: someSelector];
if (sig) {
    invocation = [NSInvocation invocationWithMethodSignature: sig];
    [invocation setTarget: delegate];
    [invocation setSelector: someSelector];
}

// 引数をセットして実行する
[invocation setArgument: firstArgument atIndex: 2];
[invocation setArgument: secondArgument atIndex: 3];
[invocation invoke];

これならデリゲートと成功/失敗コールバックを渡して中でNSInvocation作ってsendAsynchronousRequestのcompleteBlock内でよしなにコールバックを実行してあげればいい。


ということでHttpClientライブラリを作ってみた。
waka/CCHttpClient

こんな感じでデリゲートとセレクタを渡して使える。

#import "CCHttpClient"

- (void) get: (NSString *)url
{
    CCHttpClient *client = [CCHttpClient clientWithUrl: url];
    [client getWithDelegate: @{@"foo": @"bar"} // Query parameters as NSDictionary
                    headers: @{@"application/json": @"Accept"} // HTTP headers as NSDictionary
                   delegate: self
                    success: @selector(handleGetSuccess:result:)
                    failure: @selector(handleGetFailure:error:)];
}

- (void) handleGetSuccess: (NSHTTPURLResponse *)response result: (NSData *)result
{
    [SVProgressHUD: showSuccessWithStatus: @"Success!"];
    id json = [CCHttpClient responseJSON: result]; // If want JSON data
    [self updateView: json];
}

- (void) handleGetFailure: (NSHTTPURLResponse *)response result: (NSError *)error
{
    [SVProgressHUD: showErrorWithStatus: @"Failure!"];
}

同じようにPOST/PUT/DELETEも使えます。
JSONの扱いどうしようと思ったけど、そこはJSのXHRと同じくレスポンスをそのまま返しちゃって、アプリ側でJSONとして使えばいいかと。