Uploads using backgroundSessionConfiguration and NSURLSessionUploadTask cause app to crash

10,488

Solution 1

Okay, so this was kind of just me being foolish and not thorough here:

1) I'd set an exception breakpoint to get stack traces that was preventing me from see the actual exception error printout -- oops.

2) Can't use version of uploadTaskWithRequest that has a completion callback for a backgroundSessionConfiguration (not surprising but still not well documented).

3) Write your PNG data to /var/... and provide it to uploadTaskWithRequest with file:///var/... (this is just awkward because you don't often need to convert between the two for a single sequence of commands)

Happy to put up a NSUrlSessionUploadTask sample code here, since there seems to be zero of them on the entire interwebs. LMK if anyone wants that.

Solution 2

As requested, background uploading example. Be sure to implement NSURLSessionDelegate and NSURLSessionTaskDelegate as needed.

NSMutableArray *unsentPhotos = (NSMutableArray*)[sendingMessage objectForKey:@"unsentPhotos"];
TMessage *message = (TMessage*)[sendingMessage objectForKey:@"message"];
message.sendStatus = MS_PENDING_IMAGE_UPLOAD;

for (int i = 0; i < [unsentPhotos count]; i++) {
    NSString *fileName = [unsentPhotos objectAtIndex:i];
    NSLog(@"Initiating file upload for image %@", fileName);

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *srcImagePath = [NSString stringWithFormat:@"%@/messageCache/%@", [paths objectAtIndex:0], fileName];
    NSString *dataSrcImagePath = [srcImagePath stringByAppendingString:@".tmp"];

    //Encode file to data
    NSData *imageData = [NSData dataWithContentsOfFile:srcImagePath];
    if (!([imageData writeToFile:dataSrcImagePath atomically:YES])) {
        NSLog(@"Failed to save file.");
    }

    //Prepare upload request
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:@"https://blah.com"]];
    [request setHTTPMethod:@"PUT"];
    [request setValue:globalAPIToken forHTTPHeaderField:@"access_token"];
    [request setValue:[AppDelegate getMyUserID] forHTTPHeaderField:@"userid"];
    [request setValue:[NSString stringWithFormat:@"%d", message.teamID] forHTTPHeaderField:@"teamId"];
    [request setValue:[NSString stringWithFormat:@"%d", message.emailID] forHTTPHeaderField:@"messageId"];
    [request setValue:fileName forHTTPHeaderField:@"fileName"];
    [request setValue:@"1" forHTTPHeaderField:@"basefile"];

    if (i == 0) {
        //If this is the first upload in this batch, set up a new session

        //Each background session needs a unique ID, so get a random number
        NSInteger randomNumber = arc4random() % 1000000;
        NSURLSessionConfiguration *config = [NSURLSessionConfiguration backgroundSessionConfiguration: [NSString stringWithFormat:@"testSession.foo.%d", randomNumber]];
        config.HTTPMaximumConnectionsPerHost = 1;
        session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];
        //Set the session ID in the sending message structure so we can retrieve it from the
        //delegate methods later
        [sendingMessage setValue:session.configuration.identifier forKey:@"sessionId"];
    }
    uploadTask = [session uploadTaskWithRequest:request fromFile:[NSURL URLWithString:[NSString stringWithFormat:@"file://%@", dataSrcImagePath]]];
    [uploadTask resume];
}
Share:
10,488
Dinkman123
Author by

Dinkman123

Updated on June 27, 2022

Comments

  • Dinkman123
    Dinkman123 almost 2 years

    I'm trying out the new fancy iOS 7 background uploading using NSURLSessionUploadTask and it seems to work when I run with defaultSessionConfiguration, but once I try backgroundSessionConfiguration it crashes at the line where I call uploadTaskWithRequest:

    Here is the code sample below. Oddly, while there are myriad downloadTaskWithRequest examples online, I cannot find a single one that combines background and uploading together.

    //Create a session w/ background settings
    NSURLSessionConfiguration *config = [NSURLSessionConfiguration backgroundSessionConfiguration:@"identifierString.foo"];
    NSURLSession *upLoadSession = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];
    
    //Create a file to upload
    UIImage *image = [UIImage imageNamed:@"[email protected]"];
    NSData *imageData = UIImagePNGRepresentation(image);
    NSFileManager *fileManager = [NSFileManager defaultManager];
    NSArray *URLs = [fileManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask];
    NSString *documentsDirectory = [[URLs objectAtIndex:0] absoluteString];
    NSString *filePath = [documentsDirectory stringByAppendingString:@"testfile.png"];
    [imageData writeToFile:filePath atomically:YES];
    
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:@"https://file.upload/destination"]];
    [request setHTTPMethod:@"PUT"];
    NSURLSessionUploadTask *uploadTask = [upLoadSession uploadTaskWithRequest:request fromFile:[NSURL URLWithString:filePath] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        //code
    }];
    
    [uploadTask resume];
    

    This code is crashing at the line with uploadTaskWithRequest: ... just before it gets to the resume line at the end.

    Oddly, this seems to work OK when I use any config type other than backgroundSessionConfiguration. Help needed!

    Thanks in advance.

  • hemc4
    hemc4 about 10 years
    +1, Thanks for your pointers, i am stuck in same task,posted my question here but didn't got any satisfactory answer, can you please put your sample code?
  • Dinkman123
    Dinkman123 about 10 years
    Got you. See answer I just posted.
  • Aaron Brager
    Aaron Brager almost 10 years
    You'd probably want to avoid creating the request if the write to disk failed.
  • KyleStew
    KyleStew almost 10 years
    Exactly the same gotchas I ran into. I wrote a blog post because there is so much lacking info on this feature. medium.com/@KyleRStewart/zombie-uploads-with-ios-dd3b1f6b66
  • Xcoder
    Xcoder over 9 years
    @Dinkman can you answer this stackoverflow.com/questions/25311736/…
  • Michael Wildermuth
    Michael Wildermuth over 8 years
    You use NSURL fileURLWithPath to add the file:// for you instead of hard coding it with a stringWithFormat. So it would look like this instead: uploadTask = [session uploadTaskWithRequest:request fromFile:[NSURL fileURLWithPath:dataSrcImagePath];
  • serhii
    serhii about 7 years
    @Dinkman123, I am stuck in same issue with you. could you post sample code on here? The link you posted answer is not working. thanks