壊れたメガネ

ホッチキスの達人の意識の高いブログ。

iOS プログラミングメモ; HTTP を扱う

概要

HTTP リクエストを発行しレスポンスを受信するまでのプログラムの流れはだいたい次の通り。

  1. リクエストヘッダ(NSURLRequest)を作る
  2. NSURLConnection のクラスメソッドを用いリクエストを発行する
  3. レスポンスヘッダ(NSURLResponse)とレスポンスボディを取得する

Request オブジェクトを作る

NSMutableURLRequest を用いる。
NSMutableURLRequest は親クラスである NSURLRequest に比べリクエストボディ(POSTデータ)や HTTP ヘッダの設定が容易に行える。

NSData *query = [[NSString stringWithFormat:@"foo=bar&baz=%@", @"foobar"]
                       dataUsingEncoding: NSUTF8StringEncoding];
NSMutableURLRequest *request = [NSMutableURLRequest
                      requestWithURL:[NSURL URLWithString:@"http://localhost/test/"]
                      cachePolicy:NSURLRequestUseProtocolCachePolicy
                      timeoutInterval:60.0];
[request setHTTPMethod:@"POST"];
[request setValue:@"application/x-www-form-urlencoded" 
                      forHTTPHeaderField:@"Content-Type"];
[request setValue:[NSString stringWithFormat:@"%d", [query length]] 
                      forHTTPHeaderField:@"Content-Length"];
[request setHTTPBody:query];

同期方式

NSURLConnection.sendSynchronousRequest:returningResponse:error: を用いる。
同期方式は sendSynchronousRequest を呼び出したスレッドにおいて、リクエスト・レスポンス送受信が逐次処理される。 仮に UI スレッドでこのメソッドを呼び出すと、呼び出し元に制御を返すまでブロックされ、画面はフリーズする。

NSHTTPURLResponse *httpResponse;

/* HTTP リクエスト送信 */
NSData *contents = [NSURLConnection sendSynchronousRequest:request 
                      returningResponse:&httpResponse error:nil];
NSString *contentsString = [[NSString alloc] initWithData:contents encoding:NSUTF8StringEncoding];
NSLog(@"contents:\n%@", contentsString);

/* HTTP レスポンスヘッダ取得 */
NSDictionary *headers = httpResponse.allHeaderFields;
for (id key in headers) {
    NSLog(@"%@: %@", key, [headers objectForKey:key]);
}

非同期方式

NSURLConnectionDelegate プロトコルを実装する。 NSURLConnection.initWithRequest で接続されるが、同期方式と違い別スレッドでリクエスト・レスポンス送受信が処理されるためブロックされない。

@interface HTTPDownloadDelegate : NSObject<NSURLConnectionDelegate> {
    NSMutableData *contents;
}

@end

@implementation HTTPDownloadDelegate

/* レスポンスヘッダを受け取る */
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
    // (NSHTTPURLResponse *)にキャストする
    NSHTTPURLResponse *httpResopnse = (NSHTTPURLResponse *)response;
    // レスポンスヘッダを列挙
    NSDictionary *headers = httpResopnse.allHeaderFields;
    for (id key in headers) {
        NSLog(@"%@: %@", key, [headers objectForKey:key]);
    }
}

/* データを受け取る度に呼び出される */
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
    [contents appendData:data];
}

/* データを全て受け取ると呼び出される */
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    NSString *csvContents = [[NSString alloc] initWithData:contents
                      encoding:NSShiftJISStringEncoding];
    NSLog(@"%@", csvContents);
}

@end

int main(int argc, char *argv[]) {
    /* リクエストを作る */
    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://localhost/"]];

    /* 上で定義した NSURLConnectionDelegate のインスタンスを作る */
    HTTPDownloadDelegate *delegate = [[HTTPDownloadDelegate alloc] init];

    /* リクエストを送信する。 
     * この main スレッドから分岐し別のスレッドでリクエスト・レスポンスの送受信が行われる。
     * そのためアプリはここでブロックされない。 */
    NSURLConnection *connection = [[NSURLConnection alloc]
                      initWithRequest:request delegate:delegate];
    if (!connection) NSLog(@"failed to create connection");

    [[NSRunLoop currentRunLoop] run];
}

参考