NSURLSessionTask nigdy nie oddzwania po przekroczeniu limitu czasu podczas korzystania z konfiguracji w tle

Używam NSURLSessionDownloadTask z sesjami w tle, aby spełnić wszystkie moje prośby o odpoczynek. W ten sposób mogę używać tego samego kodu bez konieczności myślenia o mojej aplikacji w tle lub na pierwszym planie.

Mój back-end jest martwy od jakiegoś czasu, i skorzystałem z okazji, aby sprawdzić, jak NSURLSession zachowuje się z timeouts.

Ku mojemu całkowitemu zdziwieniu, żaden z moich telefonów zwrotnych nigdy nie zostanie wywołany. Niezależnie od tego, jaki timeout ustawiłem na NSURLRequest lub na NSURLSessionConfiguration, nigdy nie dostaję żadnego połączenia zwrotnego z iOS mówiąc mi, że prośba skończyła się z limitem czasu.

To znaczy, kiedy zaczynam NSURLSessionDownloadTask sesję w tle. To samo dzieje się aplikacja znajduje się w tle lub na pierwszym planie.

Przykładowy kod:

- (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);
}

Jednak, gdy używam sesji domyślnej, wtedy dostaję błąd wywołania zwrotnego po 30sekundach(timeout, który ustawiłem na poziomie żądania).

Przykładowy kod:

- (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);
}

Nie mogę znaleźć w dokumentacji niczego, co by sugerowało że limit czasu powinien zachowywać się inaczej podczas korzystania z sesji w tle.

Czy ktoś też wpadł na ten problem? To błąd czy funkcja?

Rozważam stworzenie raportu o błędzie, ale zazwyczaj otrzymuję informację zwrotną znacznie szybciej NA SO (kilka minut) niż na bug reporter (sześć miesięcy).

Pozdrawiam,

Author: Thibault D., 2014-04-25

5 answers

Od iOS8, NSUrlSession w trybie tła nie wywołuje tej metody delegata, jeśli serwer nie odpowiada. -(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
Pobieranie / przesyłanie pozostaje bezczynne w nieskończoność. Delegat ten jest wywoływany na iOS7 z błędem, gdy serwer nie odpowiada.

Ogólnie rzecz biorąc, sesja w tle NSURLSession nie zawiedzie zadania, jeśli coś idzie nie tak na podsłuchu. Raczej nadal szuka dobry czas, aby uruchomić żądanie i ponowić próby w tym czasie. To trwa do czasu wygaśnięcia limitu czasu zasobu (czyli wartości właściwość timeoutIntervalForResource w konfiguracji NSURLSessionConfiguration obiekt używany do utworzenia sesji). Aktualna wartość domyślna dla tego wartość to jeden tydzień!

Cytowane informacje zaczerpnięte z tego źródła

Innymi słowy, zachowanie braku czasu w iOS7 było nieprawidłowe. W kontekście sesji w tle, bardziej interesujące jest, aby nie zawieść natychmiast z powodu sieci problemy. Tak więc od iOS8, zadanie NSURLSession jest kontynuowane, nawet jeśli napotka timeouty i utratę sieci. Trwa on jednak do czasu osiągnięcia timeoutIntervalForResource.

Więc zasadniczo timeoutIntervalForRequest nie będzie działać w tle sesji, ale timeoutIntervalForResource będzie.

 17
Author: Utsav Dusad,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-21 05:10:52

Limit czasu dla DownloadTask jest wyrzucany przez NSURLSessionTaskDelegate, a nie NSURLSessionDownloadDelegate

Aby wyzwolić limit czasu (-1001) podczas zadania pobierania:

Poczekaj, aż rozpocznie się pobieranie. procentowe pobieranie danych wywoła:

URLSession:downloadTask:didWriteData: totalBytesWritten:totalBytesExpectedToWrite:

Następnie wstrzymaj całą aplikację w debugerze XCode.

Poczekaj 30 sekund.

Rozpakuj aplikację za pomocą debugera XCode buttons

Połączenie http z serwera powinno się wyłączyć i wyzwalać:

-1001 "żądanie wygasło."

#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
Author: brian.clear,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-18 16:04:55

Istnieje jedna metoda w UIApplicationDelegate, która poinformuje Cię o procesie w tle.

-(void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler

Jeśli jest więcej niż jedna sesja, możesz zidentyfikować swoją sesję przez

if ([identifier isEqualToString:@"com.mydomain.myapp.mySessionIdentifier"]) 

Używana jest jeszcze jedna metoda, aby okresowo powiadamiać o postępie .Tutaj możesz sprawdzić stan 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
Author: Jayaprada,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-25 11:55:13

Podobnie jak ty, aplikacja, nad którą pracuję, zawsze używa sesji w tle. Jedną rzeczą, którą zauważyłem, jest to, że timeout działa poprawnie, jeśli przerywa działające połączenie, tzn. transfer rozpoczął się pomyślnie. Jeśli jednak uruchomię zadanie pobierania adresu URL, który nie istnieje,nie skończy się to.

Biorąc pod uwagę, że mówiłeś, że Twój backend był martwy przez jakiś czas, to brzmi jak to, co widziałeś.

Jest dość łatwy do odtworzenia. Ustaw limit czasu na jakieś 5 sekund. Dzięki poprawnemu adresowi URL otrzymasz pewne aktualizacje postępu, a następnie zobaczysz limit czasu. Nawet z sesją w tle. Z nieprawidłowym adresem URL po prostu zamilknie, gdy tylko zadzwonisz do CV.

 3
Author: Phil Viso,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-02 04:45:05

Doszedłem do tego samego problemu. Jednym z rozwiązań, które znalazłem, jest użycie dwóch sesji, jednej do pobierania na pierwszym planie przy użyciu domyślnej konfiguracji i jednej do pobierania w tle z konfiguracją w tle. Po przejściu na tło / pierwszy plan Wygeneruj dane wznowienia i przekaż je z jednego do drugiego. Ale zastanawiam się, czy znalazłeś inne rozwiązanie.

 0
Author: Thodoris,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-27 09:07:15