hachinoBlog

hachinobuのエンジニアライフ

iOS5系でNSURLConnectionを使った場合のタイムアウト設定方法

ネットワーク処理でNSURLConnectionを使っているとiOS5系でのみ設定したタイムアウト時間よりも遥かに長く待たされた。
どうやらアップルの仕様でNSURLRequestでPOSTかつbodyに値を設定するとそうなるみたい。

問題のコードは下記

  NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] initWithURL:url] autorelease];
  [request setTimeoutInterval:10]; //タイムアウトを10秒に設定
  [request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
  [request setHTTPMethod:@"POST"]; //POSTメソッド
  [request setHTTPBody:postData];  //bodyに値をセット
  
  NSURLResponse* response;
  NSError* error = nil;
  //通信
  NSData* result = [NSURLConnection sendSynchronousRequest:urlRequest
										   returningResponse:&response
													   error:&error];

ネットワーク状態の悪いところでやると240秒待たされた。
クラスメソッドを使っていたのでキャンセルもできない。
(sendSynchronousRequestなので同期処理だが、呼び出し元でバックグラウンドスレッドを生成している。)
iOS5系のタイムアウト処理を10秒にしたかったのでタイマーで下記のように設定しました。

  NSURLConnection *connection = [[[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO] autorelease];
  self.connection = connection;  //NSURLConnectionインスタンス
  if (self.timer) {
      [self.timer invalidate];
  }
    
  //iOS5系はタイムアウト時間を設定できないので非同期メソッドとタイマーを使用して対応
  self.timer = [NSTimer scheduledTimerWithTimeInterval:10 target:self selector:@selector(connectionTimeout:) userInfo:nil repeats:NO];
    
    self.receiveData = [NSMutableData data]; //デリゲートでレスポンスをappendするインスタンス
    self.error = nil;
    [self.connection start];
    
    self.isConnection = YES;  //ネットワーク処理が終わったかどうかの判定

    //終わるまで待つ
    while (self.isConnection) {
        [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1f]];
    }
-------------------------------------------------------------------------------------------------------------

    //タイマーから呼ばれるconnectionTimeoutメソッド
    - (void)connectionTimeout:(NSTimer *)timer
    {
      [self.connection cancel];
      [timer invalidate];
      timer = nil;
      self.isConnection = NO;
    }

    #pragma mark - NSURLConnection Delegate
    - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
    {
      //receiveData初期化
      [self.receiveData setLength:0];
    }

    - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
    {
      [self.receiveData appendData:data];
    }

    - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
    {
      [self.timer invalidate];
      self.timer = nil;
      self.isConnection = NO;  
    }

ようはNSURLConnectionのインスタンス生成して非同期で通信させてタイマーで設定した時間までに何も反応無ければキャンセルするってだけです。
本来バックグラウンドスレッドで同期ネットワーク処理をしたかったのでNSRunLoopを使って終わるまで待ったりしています。