-
Notifications
You must be signed in to change notification settings - Fork 336
6.1 HTTPリクエストの基礎
この章では、iOSにおけるHTTPリクエストの投げ方について説明します。
Appleの公式ドキュメントはこちらを参照ください
FoundationフレームワークにおけるURLアクセスに関するクラスは以下のものがあります。
プロトコルとしては、http, https, ftp, fileのプロトコルをサポートし、認証やキャッシュ、クッキーの処理などを行うクラスもあります。 HTTPリクエストを投げて、処理をするだけの場合を想定した時に用いるクラスは
- NSURLRequest (URLリクエストのラッパー、HTTPheaderやbodyの追加などを行う)
- NSURLResponse (URLレスポンスのラッパー)
- NSURLConnection (リクエストを受けて通信を行い、結果を返すなど実際の通信を行うクラス)
となります。また、URLをラップしたクラスであるNSURLも用いられます。これらのクラスを用いてHTTP通信を行う時の処理のフローは以下のようになります。
- NSURL, NSURLRequestを用いてHTTPリクエストを生成
- NSURLConnectionに1.で生成したリクエストを渡して通信スタート
- 通信の結果を受け取り、処理を行う。
この章では、このフローに沿ってHTTP通信の解説を行います。
HTTP通信を行う際に何かしらサーバーが必要かと思います。リクエストの詳細を表示したりレスポンスを制御するサーバーがあれば今回は事足りるのですが、何もないよと言う方は以下のPlackアプリをご利用ください
#!/usr/bin/env perl
use strict;
use warnings;
use Data::Dumper;
my $app = sub {
my $env = shift;
print Dumper $env;
return [200,['Content-Type' => 'text/plain'], ['Hello World']];
};
return $app;
コマンドラインで
plackup (保存したファイル名)
とすると、http://127.0.0.1:5000/ でHelloWorldと表示される簡単なサーバーが立ち上がります。 またサーバー側のコンソールにはヘッダなどの情報が表示されると思います。
HTTPリクエスト生成のフローは
- NSURLオブジェクトの生成
- NSURLRequestオブジェクトの生成
- 必要なパラメータの付与
という形になります。
とても簡単です。
NSURL *url = [NSURL URLWithString:@"http://mixi.jp"];
とすることで、NSURLオブジェクトを作ることができます。
メソッド名 | 説明 |
---|---|
+ (id)URLWithString:(NSString *)URLString | URLの文字列からNSURLオブジェクトを作る |
またNSURLオブジェクトはURLの一部にアクセスすることも可能です。
NSURL *url = [NSURL URLWithString:@"http://mixi.jp:9000/hoge/fuga/piyo?id=fugafuga&key=value"];
NSLog(@"%@", url); // http://mixi.jp:9000/hoge/fuga/piyo?id=fugafuga&key=value
NSLog(@"%@", [url pathComponents]); // ["/", hoge, fuga, piyo]
NSLog(@"%@", [url baseURL]); // null
NSLog(@"%@", [url scheme]); // http
NSLog(@"%@", [url host]); // mixi.jp
NSLog(@"%@", [url port]); // 9000
NSLog(@"%@", [url query]); // id=fugafuga&key=value
その他のオプションについてはこちらをご覧ください。
NSURLからNSURLRequestを作るには、以下のようにします
NSURLRequest *request1 = [NSURLRequest requestWithURL:url]; //シンプルな方法
NSURLRequest *request2 = [NSURLRequest requestWithURL:url cachePolicy:NSURLCacheStorageAllowedInMemoryOnly timeoutInterval:30.0]; // キャッシュの方法やタイムアウトの時間を指定する方法
NSURLRequestオブジェクトは生成後に変更不可能なイミュータブルナクラスです。パラメータの付与を行う際にはミュータブルなNSMutableURLRequestクラスを用います。
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
HTTPヘッダやBodyを付与する場合は以下のようにします。それぞれNSMutableURLRequestのメソッドです
HTTPヘッダを付与する場合
[request setValue:@"plain/text" forHTTPHeaderField:@"Content-Type"];
メソッド名 | 説明 |
---|---|
- (void)setValue:(NSString *)value forHTTPHeaderField:(NSString *)field |
リクエストにHTTPヘッダを追加する。第一引数にはヘッダの値を、第二引数にはヘッダのフィールド名を渡す |
Bodyを付与する場合
NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
[request setHTTPBody:data];
メソッド名 | 説明 |
---|---|
- (void)setHTTPBody:(NSData *)data; |
リクエストにBodyを追加する。引数はNSData型となるため、文字列を渡したい場合はdataUsingEncodingを用いて一度変換する必要がある |
ここまでで、以下のようなリクエストを作ることができました。
NSURL *url = [NSURL URLWithString:@"http://mixi.jp:9000"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
NSData *data = [@"hoge fuga piyo" dataUsingEncoding:NSUTF8StringEncoding];
[request setHTTPMethod:@"POST"];
[request setValue:@"x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
[request setValue:[NSString stringWithFormat:@"%d", [data length]] forHTTPHeaderField:@"Content-Length"]; // PostでContent-Lengthを設定する
[request setHTTPBody:data];
ここまでで生成したリクエストを実際に投げて、そのレスポンスを取得します。
サーバーとの通信を行うのはNSURLConnectionクラスが行います。クライアントサイドにおける通信には、大きく分けて同期通信(synchronous)と非同期通信(asynchronous)の二種類があります。同期通信は通信が完了するまで次のステップに進まず待ち続けます。そのためメインスレッドで実行するとUIが更新されない、固まるといったバグを引き起こします。一方で非同期通信は通信の完了を待たずに次のステップへ進み、別スレッドでロードを行います。そのためメインスレッドなどで実行しても固まる心配は少ないです。ロード完了後の処理はdelegateメソッドを呼ぶパターンとBlocksを実行するパターンの二つがあります。 ここでは、同期通信、非同期通信のBlocksパターンを紹介します。
同期的に読み込むにはクラスメソッドであるsendSynchronousRequest:returningResponse:errorを用います。
NSHTTPURLResponse *response;
NSError *error;
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
メソッド名 | 説明 |
---|---|
sendSynchronousRequest:returningResponse:error: |
引数として渡したリクエストを同期的に実行する。戻り値はレスポンスのデータ(NSData型)。また引数としてURLResponseとNSErrorのポインタを渡すことで、レスポンスのヘッダ情報やエラーなどを取得できる |
このメソッドの前後にNSLogで実行時間を表示してみます。
2013-04-30 15:42:09.151 HTTPSample[80295:c07] before
// ----- ここで実行中 ----- //
2013-04-30 15:42:09.170 HTTPSample[80295:c07] done
およそ19[msec]ほどですが、待ち時間が発生していることが分かります。(リクエストはlocalhostではなくVMに向けました)
非同期で読み込むにはクラスメソッドであるsendAsynchronousRequest:queue:completionHandlerを用います。
NSOperationQueue *queue = [NSOperationQueue new];
[NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *res, NSData *data, NSError *error) {
NSLog(@"%@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
}];
メソッド名 | 説明 |
---|---|
sendAsynchronousRequest:queue:completionHandler: |
引数として渡したリクエストを非同期的に実行する。実行は引数として渡したNSOperationQueue上で行われ、読み込みが完了するとblocksが実行されます |
実行後blockに渡ってくるData, response, errorは同期実行したときのものと同様です。
※ NSOperationQueueについて
- NSOperationQueueとはNSOperationとあわせてiOSでバックグラウンドで並列実行させたいときに用いられました。NSOperationをサブクラス化して処理を記述し、NSOperationQueueに処理を追加していくという使い方をしていました。しかしスレッド管理などが煩わしく現在はGCD(Global Central Dispatch)というマルチスレッドのためのフレームワークを用いることが一般的です。GCDについては第9回の時に説明します。
以下のようにNSLogを追加し、どの順番で実行されているかを確認しましょう。
NSOperationQueue *queue = [NSOperationQueue new];
NSLog(@"before");
[NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *res, NSData *data, NSError *error) {
NSLog(@"done");
}];
NSLog(@"after");
結果は以下の通りになります
2013-04-30 15:55:52.440 HTTPSample[80498:c07] before
2013-04-30 15:55:52.441 HTTPSample[80498:c07] after
2013-04-30 15:55:53.457 HTTPSample[80498:1303] done
beforeに続けてすぐにafterが呼ばれているのが分かるかと思います。そして通信が完了した後にdoneが呼ばれているのが分かるかと思います。
NSURLConnectionを用いて通信を行った後は、そのデータを処理しましょう。send(A)synchronousRequestの結果はNSData型で渡されるので文字列として処理したい場合は次のように変換します。
NSString *responseString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
encodingはそれぞれ適切なエンコードを入れてください。
通信を行ったとき、通信結果のデータと共にNSURLResponseとNSErrorが渡されます。
NSURLResponseはリクエストに対するレスポンスのラッパーで、レスポンスのMIMEタイプなどを知ることができます。。NSURLConnectionで渡したリクエストのプロトコルがhttp, httpsの場合このレスポンスの実態はNSURLHTTPResponseとなり、HTTPレスポンスヘッダなどが含まれるようになります。
NSErrorは汎用的なエラー型です。エラードメインやエラーコード、エラーの理由に関する記述などが含まれます。エラーがない時はnilとなるので、エラーのとき、エラーでないときで処理を分ける場合は以下のようにすることが一般的です。
NSError *error;
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
if( error ){
// エラー処理
} else {
// 通常の処理
}
NSErrorにはHTTPのエラーコードなどは含まれないので注意してください。
ここまでで、基本的なHTTPリクエストについて抑えることができたので、その内容を用いた演習です。
概要
どこか適当なサーバー(先ほど立てたPlackなど)に対してリクエストを投げ、レスポンス結果をUILabelに表示するアプリを作ってください。
- リクエストは非同期で実行してください
- 完成したら、エラーが返却された時の処理を追加してください
- 赤文字でエラーの理由などを記述してください
非同期な通信(sendSynchronousRequest)を行った時、完了ブロック(completionHandler)も渡したqueueの中で実行されます。 iOSのUIなどの描画を行う際には、メインスレッド上で行わないといけないため、完了ブロックの中で更新処理を行うと更新されません。 そこで、再描画をメインスレッドで実行します。以下のようにすることで、メインスレッドで処理を行うことができます。
NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
[mainQueue addOperationWithBlock:^{
// ここにUIの処理を記述
}];
はじめに
-
導入
-
1.3 UIViewController1 UIViewController のカスタマイズ(xib, autoresizing)
-
UIKit 1 - container, rotate-
-
UIKit 2- UIView -
-
UIKit 3 - table view -
-
UIKit 4 - image and text -
-
ネットワーク処理
-
ローカルキャッシュと通知
-
Blocks, GCD
-
設計とデザインパターン
-
開発ツール
-
テスト
-
In-App Purchase
-
付録