Skip to content
This repository has been archived by the owner on Jun 20, 2023. It is now read-only.

6.1 HTTPリクエストの基礎

ginrou edited this page May 14, 2013 · 5 revisions

この章では、iOSにおけるHTTPリクエストの投げ方について説明します。

Appleの公式ドキュメントはこちらを参照ください

http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/URLLoadingSystem/URLLoadingSystem.html

HTTPリクエスト概要

FoundationフレームワークにおけるURLアクセスに関するクラスは以下のものがあります。

http://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/URLLoadingSystem/Concepts/art/nsobject_hierarchy.gif

URL Loading System Programming Guide より引用

プロトコルとしては、http, https, ftp, fileのプロトコルをサポートし、認証やキャッシュ、クッキーの処理などを行うクラスもあります。 HTTPリクエストを投げて、処理をするだけの場合を想定した時に用いるクラスは

  • NSURLRequest (URLリクエストのラッパー、HTTPheaderやbodyの追加などを行う)
  • NSURLResponse (URLレスポンスのラッパー)
  • NSURLConnection (リクエストを受けて通信を行い、結果を返すなど実際の通信を行うクラス)

となります。また、URLをラップしたクラスであるNSURLも用いられます。これらのクラスを用いてHTTP通信を行う時の処理のフローは以下のようになります。

  1. NSURL, NSURLRequestを用いてHTTPリクエストを生成
  2. NSURLConnectionに1.で生成したリクエストを渡して通信スタート
  3. 通信の結果を受け取り、処理を行う。

この章では、このフローに沿って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リクエストの生成

HTTPリクエスト生成のフローは

  1. NSURLオブジェクトの生成
  2. NSURLRequestオブジェクトの生成
  3. 必要なパラメータの付与

という形になります。

NSURLオブジェクトの生成

とても簡単です。

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

その他のオプションについてはこちらをご覧ください。

https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSURL_Class/Reference/Reference.html

NSURLRequestの生成

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を用いた通信

ここまでで生成したリクエストを実際に投げて、そのレスポンスを取得します。

サーバーとの通信を行うのは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と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に表示するアプリを作ってください。

https://raw.github.com/mixi-inc/iOSTraining/master/Doc/Images/6.1/url_connection_sample.png

  • リクエストは非同期で実行してください
  • 完成したら、エラーが返却された時の処理を追加してください
  • 赤文字でエラーの理由などを記述してください

注意点

非同期な通信(sendSynchronousRequest)を行った時、完了ブロック(completionHandler)も渡したqueueの中で実行されます。 iOSのUIなどの描画を行う際には、メインスレッド上で行わないといけないため、完了ブロックの中で更新処理を行うと更新されません。 そこで、再描画をメインスレッドで実行します。以下のようにすることで、メインスレッドで処理を行うことができます。

NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
[mainQueue addOperationWithBlock:^{
    // ここにUIの処理を記述
}];

はじめに

  1. iOSについて

  2. Xcode最初のステッフ

  3. 導入

  4. Objective C の基礎

  5. メモリ管理

  6. 1.3 UIViewController1 UIViewController のカスタマイズ(xib, autoresizing)

  7. 1.3 UIViewController1 UIViewController のカスタマイズ(storyboard)

  8. UIViewController2 - ModalViewController

  9. UIViewController2 - ModalViewController(storyboard)

  10. UIViewController3 - ライフサイクル

  11. HomeWork 1 Objective C の基本文法

  12. HomeWork 2 UIViewControllerとModalViewController

  13. HomeWork 3 UIViewController + Animation

  14. UIKit 1 - container, rotate-

  15. UINavigationController

  16. UITabController

  17. Custom Container View Controller

  18. Supporting Multiple Interface Orientations

  19. HomeWork 1 - タブバーからモーダルビューを表示する

  20. HomeWork 2 - NavigationController

  21. HomeWork 2.3 デバイスことに回転対応

  22. UIKit 2- UIView -

  23. UIView

  24. UIView のカスタマイズ

  25. UIView Animation

  26. HomeWork 1 - UIScrollView

  27. UIKit 3 - table view -

  28. UITableView について

  29. UITableViewとNavigationController

  30. custom UITableViewCell の作成

  31. UITableViewのその他のオプション、カスタマイズ

  32. HomeWork 1 - Dynamic height with a custom uitableviewcell

  33. UIKit 4 - image and text -

  34. UIImagePickerController

  35. Assets Library

  36. UITextFiled, UITextView

  37. KeyboardNotification

  38. Homework 1 - フォトの複数枚選択

  39. ネットワーク処理

  40. NSURLConnection

  41. JSONのシリアライズとデシリアライズ

  42. UIWebView

  43. ローカルキャッシュと通知

  44. NSUserDefaults, Settings Bundle

  45. NSFileManager

  46. Key Value Observing

  47. NSNotification、NSNotificationCenter を用いた通知

  48. UILocalNotification

  49. Blocks, GCD

  50. Blocks

  51. GCD

  52. 【演習】GCD,-Blocksを用いたHTTPリクエストマネージャの作成

  53. 設計とデザインパターン

  54. クラス設計 1

  55. クラス設計 2

  56. [クラス設計演習] (https://github.com/mixi-inc/iOSTraining/wiki/9.3-%E3%82%AF%E3%83%A9%E3%82%B9%E8%A8%AD%E8%A8%88%E6%BC%94%E7%BF%92)

  57. 開発ツール

  58. Instruments, デバッガ

  59. CocoaPods

  60. テスト

  61. iOS開発におけるテスト

  62. GHUnit

  63. Kiwi

  64. KIF

  65. In-App Purchase

  66. In-App Purchase

  67. 付録

  68. Tips of Xcode

  69. Auto Layout 入門

  70. Auto Layout ドリル

Edit sidebar

Clone this wiki locally