web-dev-qa-db-ja.com

NSURLSessionTaskは、バックグラウンド構成を使用している場合、タイムアウト後にコールバックしません

私はすべての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(数分))の方がはるかに早くフィードバックを受け取ります。

よろしく、

20
Thibault D.

IOS8以降、サーバーが応答しない場合、バックグラウンドモードのNSUrlSessionはこのデリゲートメソッドを呼び出しません。 -(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
ダウンロード/アップロードは無期限にアイドルのままです。このデリゲートは、サーバーが応答しない場合にエラーでiOS7で呼び出されます。

一般に、NSURLSessionバックグラウンドセッションは、ネットワーク上で問題が発生してもタスクを失敗させません。むしろ、リクエストを実行する適切な時間を探し続け、その時点で再試行します。これは、リソースのタイムアウト(つまり、セッションの作成に使用するNSURLSessionConfigurationオブジェクトのtimeoutIntervalForResourceプロパティの値)が期限切れになるまで続きます。その値の現在のデフォルトは1週間です!

このソースから引用された引用情報

つまり、iOS7でタイムアウトが発生して失敗する動作は正しくありませんでした。バックグラウンドセッションのコンテキストでは、ネットワークの問題が原因ですぐに失敗しないことがより興味深いです。したがって、iOS8以降、タイムアウトやネットワークの損失が発生した場合でも、NSURLSessionタスクは続行されます。ただし、timeoutIntervalForResourceに達するまで続きます。

したがって、基本的に、timeoutIntervalForRequestはバックグラウンドセッションでは機能しませんが、timeoutIntervalForResourceは機能します。

20
Utsav Dusad

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__);
    }
}
4
brian.clear

あなたのように、私が取り組んでいるアプリは常にバックグラウンドセッションを使用します。私が気づいたことの1つは、タイムアウトが動作中の接続を中断している場合、つまり転送が正常に開始された場合、タイムアウトは適切に機能することです。ただし、存在しないURLのダウンロードタスクを開始しても、タイムアウトしません。

バックエンドがしばらく死んでいたとあなたが言ったとすると、これはあなたが見ていたものとよく似ています。

再現はとても簡単です。タイムアウトを5秒程度に設定するだけです。有効なURLを使用すると、進行状況の更新がいくつか取得され、タイムアウトが表示されます。バックグラウンドセッションでも。無効なURLでは、resumeを呼び出すとすぐに静かになります。

3
Phil Viso

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,              
3
Jayaprada

私はまったく同じ問題を思いつきました。私が見つけた1つの解決策は、2つのセッションを使用することです。1つはデフォルト構成を使用したフォアグラウンドダウンロード用で、もう1つはバックグラウンド構成を使用したバックグラウンドダウンロード用です。バックグラウンド/フォアグラウンドに変更する場合、履歴書データを生成し、それを一方から他方に渡します。しかし、私はあなたが別の解決策を見つけたかどうか疑問に思っています。

0
Thodoris