iOS7で追加されたNSURLSessionnを使ってみよう。
デリゲート無しで気軽に通信処理が書けるので、ちょっとした通信なんかには活躍が見込めそうだ。

NSURLSessionの使い方については、このページがとてもわかり易くためになったので、大いに参考にさせていただいた。

iOS NSURLSession Example (HTTP GET, POST, Background Downlads ) - hayageek

※Swift版の記事を作成したのでリンクを貼っておく。
[iOS] NSURLSessionで通信を行う [Swift版]

画像をダウンロードする

まずはシンプルな処理から始めよう。
画像をダウンロードして、ImageViewに設定するだけの処理を行うことにする。
ボタンを押して通信を開始するようにしたいので、Storyboardでこのように設定しよう。

NSURLSession-ss-01
次にボタンとImageViewをViewControllerと結びつけておく。
@interface ViewController : UIViewController

@property (nonatomic, weak) IBOutlet UIImageView* imageView;

- (IBAction)startSession:(id)sender;

@end
これで下ごしらえは完了だ。
ここからいよいよ通信処理を記述していく。

画像を取得してImageViewを取得するためのメソッドをこのように記述する。
さて、この中にはNSURLSessionを使うための基本的かつ重要なポイントがある。
4-5行目に注目してほしい。
NSURLSessionConfiguration* config = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession* session = [NSURLSession sessionWithConfiguration:config];
セッション用のコンフィグを作成し、それを基にセッションを作成している。
とりあえずデフォルトセッションを指定しているのは今は気にしなくて良いだろう。

省略しているが、コンフィグは様々なプロパティを持っているので必要に応じて設定しよう。
例えば、タイムアウトの時間であったり、WiFiのみの接続に限るための設定であったりだ。
詳細はリファレンスを参照してほしい。

次にタスクの設定を行う。
    NSURLSessionDataTask* task =
    [session dataTaskWithURL:url
           completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
               // 省略
           }];
見ての通り、実にシンプルだ。
completionHandlerにダウンロードが完了した場合の処理を記述するだけで良い。
受け取ったデータは*dataに入っているので、ここではそれを使ってImageViewを更新している。

あとはタスクを実行するだけだ。
    [task resume];
タスクの設定を記述して、この処理を忘れてしまうことはよくあるミスなので気をつけるようにしよう。

これで、このメソッドは完成だ。

startSessionメソッドから、このメソッドを呼び出すようにして実行すれば期待通りの結果を得られるだろう。

ファイルとしてダウンロードする

前のセクションではメモリ上に画像をダウンロードした。
しかし、それだと問題がある場合もある。
数十〜数百メガのファイルをダウンロードする場合等だ。
(アプリの初回起動時に大容量のzipファイルをダウンロードすることは珍しくないだろう。)

このセクションではファイルとしてダウンロードする処理を実装する。

先ほどの場合とほぼ同じなので、特に解説はいらないだろう。
使用するタスククラスがNSURLSessionDataTaskからNSURLSessionDownloadTaskに変わっただけである。
ダウンロードが完了したファイルはtmpディレクトリにあるはずなので、好きに使用すれば良い。
※この例ではDocumentsディレクトリに移動させてるが、この手のファイルをDocumentsディレクトリに置くとリジェクトの対象になってしまうので注意すること。

デリゲートを使用してダウンロードの途中経過を知る

前のセクションのやり方だといつ処理が終わったのかがわからない。
特に大容量ファイルだとダウンロードに時間がかかるので、通信中であることをユーザに知らせたほうが良い。

一つの解決方法としては、通信開始とともにインジケータを表示し、completionHandlerの中でそれを非表示にするやりかたが考えられるだろう。
これはシンプルな実装になるので、ここでは説明するまでもないだろう。

もう一つの解決方法として、プログレスバーでダウンロードの割合を表示するやり方だ。
こちらの方がよりユーザに親切と言えるだろう。
ここではこちらのやり方を採用し、実装していく。

Storyboardをこのように変更しよう。
NSURLSession-ss-02
このProgressViewを参照できるようにViewController.hも変更し、Storyboardとの関連付けも行っておこう。
@interface ViewController : UIViewController

@property (nonatomic, weak) IBOutlet UIImageView* imageView;
@property (nonatomic, weak) IBOutlet UIProgressView* progressView;

- (IBAction)startSession:(id)sender;

@end
今回はデリゲートを使用するので、このようにデリゲートを追加しよう
@interface ViewController ()
    <NSURLSessionDownloadDelegate>
デリゲートのメソッドの実装はひとまず置いておいて、先にダウンロードする処理を記述してしまおう。
先ほどとほぼ同じ処理だ。
違うのは、タスクの設定でcompletionHandlerを指定していないくらいである。
ここだけに関して言えば、かなりシンプルになった。

次はデリゲートのメソッドを実装していこう。
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes
{
    _progressView.progress = 0;
}

- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
    float percent = (float)totalBytesWritten / (float)totalBytesExpectedToWrite;
    _progressView.progress = percent;
}

- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
{
    _progressView.progress = 1.0;
    
    // ファイルを移動(省略)
}
ここも特別難しいことは無い。
上から順に、ダウンロードがリジュームした時、ダウンロード中、ダウンロード完了時に呼ばれる。
この中でProgressViewの値を変えていけば良い。

これで当初の予定は完了だ。
しかし、もう一つNSURLSessionの素晴らしい点に触れておこう。

NSURLSessionをバックグラウンドで動作させる

NSURLSessionではアプリをバックグラウンドで動作させるのがとても簡単だ。
前のセクションのコードをたった一行だけ変えてやればいい。

リスト3−3の4行目をこう変更しよう。
NSURLSessionConfiguration* config = [NSURLSessionConfiguration backgroundSessionConfiguration:@"backgroundTask"];
たったこれだけで、バックグラウンドで動作させることが出来る。
驚きだ。

これでダウンロードに関してはひと通りのことは出来るようになった。
アップロードはこちらの記事で解説している。

[iOS] NSURLSessionでマルチパートデータを送信する

なお、NSURLSessionではメモリリークが起きる場合があるので気をつけたほうが良い。
詳しくはこちらの記事にまとめた。

[iOS] NSURLSessionのメモリリークに気をつける