私はすべてのRESTリクエストを達成するためにバックグラウンドセッションでNSURLSessionDownloadTask
を使用しています。このようにして、アプリケーションがバックグラウンドまたはフォアグラウンドにあることを考える必要なく、同じコードを使用できます。
私のバックエンドはしばらく死んでおり、その機会を利用して、タイムアウトでのNSURLSession
の動作をテストしました。
驚いたことに、私のNSURLSessionTaskDelegate
コールバックはどれも呼び出されません。 NSURLRequest
またはNSURLSessionConfiguration
に設定したタイムアウトが何であれ、リクエストがタイムアウトで終了したことを知らせるコールバックをiOSから受け取ることはありません。
つまり、バックグラウンドセッションでNSURLSessionDownloadTask
を開始したときです。同じ動作がアプリケーションがバックグラウンドまたはフォアグラウンドで発生します。
サンプルコード:
- (void)launchDownloadTaskOnBackgroundSession {
NSString *sessionIdentifier = @"com.mydomain.myapp.mySessionIdentifier";
NSURLSessionConfiguration *backgroundSessionConfiguration = [NSURLSessionConfiguration backgroundSessionConfiguration:sessionIdentifier];
backgroundSessionConfiguration.requestCachePolicy = NSURLRequestReloadIgnoringCacheData;
backgroundSessionConfiguration.timeoutIntervalForRequest = 40;
backgroundSessionConfiguration.timeoutIntervalForResource = 65;
NSURLSession *backgroundSession = [NSURLSession sessionWithConfiguration:backgroundSessionConfiguration delegate:self delegateQueue:[NSOperationQueue mainQueue]];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://www.timeout.com/"]];
request.timeoutInterval = 30;
NSURLSessionDownloadTask *task = [backgroundSession downloadTaskWithRequest:request];
[task resume];
}
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
NSLog(@"URLSession:task:didCompleteWithError: id=%d, error=%@", task.taskIdentifier, error);
}
ただし、デフォルトのセッションを使用すると、30秒(リクエストレベルで設定したタイムアウト)後にエラーコールバックが発生します。
サンプルコード:
- (void)launchDownloadTaskOnDefaultSession {
NSURLSessionConfiguration *defaultSessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
defaultSessionConfiguration.requestCachePolicy = NSURLRequestReloadIgnoringCacheData;
defaultSessionConfiguration.timeoutIntervalForRequest = 40;
defaultSessionConfiguration.timeoutIntervalForResource = 65;
NSURLSession *defaultSession = [NSURLSession sessionWithConfiguration:defaultSessionConfiguration delegate:self delegateQueue:[NSOperationQueue mainQueue]];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://www.timeout.com/"]];
request.timeoutInterval = 30;
NSURLSessionDownloadTask *task = [defaultSession downloadTaskWithRequest:request];
[task resume];
}
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
NSLog(@"URLSession:task:didCompleteWithError: id=%d, error=%@", task.taskIdentifier, error);
}
ドキュメントで、バックグラウンドセッションを使用した場合のタイムアウトの動作が異なることを示唆するものは見つかりません。
誰かがその問題にぶつかったことがありますか?それはバグですか、それとも機能ですか?
バグレポートの作成を検討していますが、通常、バグレポーター(6か月)よりもSO(数分))の方がはるかに早くフィードバックを受け取ります。
よろしく、
IOS8以降、サーバーが応答しない場合、バックグラウンドモードのNSUrlSessionはこのデリゲートメソッドを呼び出しません。 -(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
ダウンロード/アップロードは無期限にアイドルのままです。このデリゲートは、サーバーが応答しない場合にエラーでiOS7で呼び出されます。
一般に、NSURLSessionバックグラウンドセッションは、ネットワーク上で問題が発生してもタスクを失敗させません。むしろ、リクエストを実行する適切な時間を探し続け、その時点で再試行します。これは、リソースのタイムアウト(つまり、セッションの作成に使用するNSURLSessionConfigurationオブジェクトのtimeoutIntervalForResourceプロパティの値)が期限切れになるまで続きます。その値の現在のデフォルトは1週間です!
つまり、iOS7でタイムアウトが発生して失敗する動作は正しくありませんでした。バックグラウンドセッションのコンテキストでは、ネットワークの問題が原因ですぐに失敗しないことがより興味深いです。したがって、iOS8以降、タイムアウトやネットワークの損失が発生した場合でも、NSURLSessionタスクは続行されます。ただし、timeoutIntervalForResourceに達するまで続きます。
したがって、基本的に、timeoutIntervalForRequestはバックグラウンドセッションでは機能しませんが、timeoutIntervalForResourceは機能します。
DownloadTaskのタイムアウトは、NSURLSessionDownloadDelegateではなくNSURLSessionTaskDelegateによってスローされます。
downloadTaskの間にタイムアウト(-1001)をトリガーするには:
ダウンロードが開始するまで待ちます。データダウンロードのパーセンテージチャンクがトリガーされます。
RLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:
次に、XCodeデバッガーでアプリ全体を一時停止します。
30秒待ちます。
XCodeデバッガボタンを使用してアプリの一時停止を解除します
サーバーからのhttp接続はタイムアウトしてトリガーする必要があります。
-1001 "リクエストがタイムアウトしました。"
#pragma mark -
#pragma mark NSURLSessionTaskDelegate - timeouts caught here not in DownloadTask delegates
#pragma mark -
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
if(error){
ErrorLog(@"ERROR: [%s] error:%@", __PRETTY_FUNCTION__,error);
//-----------------------------------------------------------------------------------
//-1001 "The request timed out."
// ERROR: [-[SNWebServicesManager URLSession:task:didCompleteWithError:]] error:Error Domain=NSURLErrorDomain Code=-1001 "The request timed out." UserInfo={NSUnderlyingError=0x1247c42e0 {Error Domain=kCFErrorDomainCFNetwork Code=-1001 "(null)" UserInfo={_kCFStreamErrorCodeKey=-2102, _kCFStreamErrorDomainKey=4}}, NSErrorFailingURLStringKey=https://directory.clarksons.com/api/1/dataexport/ios/?lastUpdatedDate=01012014000000, NSErrorFailingURLKey=https://directory.clarksons.com/api/1/dataexport/ios/?lastUpdatedDate=01012014000000, _kCFStreamErrorDomainKey=4, _kCFStreamErrorCodeKey=-2102, NSLocalizedDescription=The request timed out.}
//-----------------------------------------------------------------------------------
}else{
NSLog(@"%s SESSION ENDED NO ERROR - other delegate methods should also be called so they will reset flags etc", __PRETTY_FUNCTION__);
}
}
あなたのように、私が取り組んでいるアプリは常にバックグラウンドセッションを使用します。私が気づいたことの1つは、タイムアウトが動作中の接続を中断している場合、つまり転送が正常に開始された場合、タイムアウトは適切に機能することです。ただし、存在しないURLのダウンロードタスクを開始しても、タイムアウトしません。
バックエンドがしばらく死んでいたとあなたが言ったとすると、これはあなたが見ていたものとよく似ています。
再現はとても簡単です。タイムアウトを5秒程度に設定するだけです。有効なURLを使用すると、進行状況の更新がいくつか取得され、タイムアウトが表示されます。バックグラウンドセッションでも。無効なURLでは、resumeを呼び出すとすぐに静かになります。
UIApplicationDelegateには、バックグラウンドプロセスについて通知するメソッドが1つあります。
-(void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler
複数のセッションがある場合は、次の方法でセッションを識別できます
if ([identifier isEqualToString:@"com.mydomain.myapp.mySessionIdentifier"])
もう1つのメソッドを使用して、進行状況を定期的に通知します。ここでは、NSURLSessionの状態を確認できます。
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
NSURLSessionTaskStateRunning = 0,
NSURLSessionTaskStateSuspended = 1,
NSURLSessionTaskStateCanceling = 2,
NSURLSessionTaskStateCompleted = 3,
私はまったく同じ問題を思いつきました。私が見つけた1つの解決策は、2つのセッションを使用することです。1つはデフォルト構成を使用したフォアグラウンドダウンロード用で、もう1つはバックグラウンド構成を使用したバックグラウンドダウンロード用です。バックグラウンド/フォアグラウンドに変更する場合、履歴書データを生成し、それを一方から他方に渡します。しかし、私はあなたが別の解決策を見つけたかどうか疑問に思っています。