5

I am getting the following crash on crashlytics which I can not reproduce on my device

Fatal Exception: NSGenericException
Task created in a session that has been invalidated

at the following line

NSURLSessionTask *task = [self.session uploadTaskWithRequest:request fromFile:filePathURL];
[task resume];
[self.session finishTasksAndInvalidate];

I handled session invalidation at the delegate method

- (void)URLSession:(NSURLSession *)session didBecomeInvalidWithError:(NSError *)error {
    // Crashlytics logging
    [CrashlyticsKit setBoolValue:true forKey:@"URLSession_didBecomeInvalid"];
    self.session = [self createNewSession];
}

- (NSURLSession *)CreateSession {
    NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:SERVER_URL];

    if (@available(iOS 11.0, *)) {
        sessionConfig.waitsForConnectivity = YES;
    }
    return [NSURLSession sessionWithConfiguration:sessionConfig delegate:self delegateQueue:nil];
}

after uploading a new build, I still have the same crash and no Crashlytics logs at "didBecomeInvalidWithError" at all!

any idea how to solve this crash?

Mahmoud Adam
  • 5,772
  • 5
  • 41
  • 62
  • Is `didBecomeInvalidWithError` _ever_ called? I guess what I'm trying to find out is: are you sure this method is implemented in some object that really is the URLSession delegate? — Also: are you conscious that a URLSession _retains_ its delegate? This causes tricky memory management issues. What are you doing about that? — Also, please show `createNewSession`. – matt Mar 11 '20 at 12:50
  • I have updated the questions and added more code. Note that the class is Singlton, i.e. the delegate exists over the application life cycle because I need to send files in multiple situations in background not only once. – Mahmoud Adam Mar 11 '20 at 13:20
  • @matt when I debug the app the code reach `didBecomeInvalidWithError` when it finish uploading the files later it works again when trying to upload files but on production I got this crash – Mahmoud Adam Mar 11 '20 at 13:29
  • So you are starting a task and invalidating the session (but hanging on to that old session reference for some reason)? And you are 100% sure that you never start another task on that session? – Rob Mar 11 '20 at 16:43
  • @Rob So you mean that as soon as I use one session multiple times while the user is using the app (not just uploading one patch of files) then I should not invalidate it as there will be used for subsequent requests later in the time line – Mahmoud Adam Mar 12 '20 at 08:52
  • I’m saying that when you invalidate a session, you cannot ever use it again (and if you attempt to do so, you’ll get that error you describe). So, if you invalidate, `nil` your `session` reference so you won’t use it again. Alternatively, if you might ever need to use it again, just don’t invalidate it. Especially in the case of background sessions, IMHO. – Rob Mar 12 '20 at 15:13

1 Answers1

1

If you invalidate a session, you cannot ever use it again. If you attempt to use a previously invalidated session, you’ll get that error you shared with us.

That leaves you with two options:

  • If you must invalidate a session, I’d recommend setting your session reference to nil so you can’t accidentally use it again. If you have to do another request later, you would instantiate a new session object.

  • If you might ever need to use the session again, you can simply refrain from invalidating it. The memory impact of keeping a single session object around is largely inconsequential. It only really an issue if you’re creating many sessions.

Frankly, background sessions are complicated enough that I’d need a pretty compelling case for dealing with multiple ones (e.g. each with its own completion handler passed to my app delegate). I’d lean towards the single background session pattern if possible.

Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • Can there be a third option of handling (catching) that exception appropriately ? – Shashikant Jan 18 '21 at 18:39
  • 1
    @Shashikant - No, you don't programmatically catch and handle exceptions, but rather excise them from your code entirely. (Swift formalizes this convention, not even offering easy ways to catch exceptions, only handling thrown errors, which despite the similarity in nomenclature, is a completely different concept.) As the [Introduction to Exception Programming](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Exceptions/Exceptions.html) says, “You usually take care of these sorts of errors with exceptions when an application is being created rather than at runtime.” – Rob Jan 18 '21 at 19:52