How to upload task in background using afnetworking

4.1k views Asked by At

I'm trying to upload large files using AFNetworking and have the upload continue when the application is in the background.

I can upload files just fine, but when I attempt to use a background configuration -- the application crashes with the following stacktrace: Exception: EXC_BAD_ACCESS (code=1, address=0x8000001f))

_CFStreamSetDispatchQueue
-[__NSCFBackgroundDataTask captureStream:]
__70-[__NSCFBackgroundDataTask _onqueue_needNewBodyStream:withCompletion:]_block_invoke_2
_dispatch_call_block_and_release
_dispatch_queue_drain
_dispatch_queue_invoke
_dispatch_root_queue_drain
_dispatch_worker_thread3
_pthread_wqthread

Here is some example code:

Note: When I use [NSURLSessionConfiguration defaultSessionConfiguration] the upload succeeds but will not continue when the application is in the background. [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:@"com.company.appname.uploadservice"] causes the application to crash.

NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:@"POST" URLString:[uploadBundle.uploadUrl absoluteString] parameters:[uploadBundle getParameters] constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
    [formData appendPartWithFileURL:uploadBundle.fileDataUrl name:uploadBundle.fileName fileName:uploadBundle.fileName mimeType:uploadBundle.mimeType error:nil];
} error:nil];

Authentication *authentication = [Authentication getInstance];
[request addValue:authentication.token forHTTPHeaderField:@"token"];
[request addValue:authentication.authorization forHTTPHeaderField:@"Authorization"];

//NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:@"com.company.appname.uploadservice"];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];

NSProgress *progress = nil;
_currentUploadTask = [manager uploadTaskWithStreamedRequest:request progress:&progress completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
    if (error) {
        NSLog(@"Error: %@", error);
    } else {
        NSLog(@"%@ %@", response, responseObject);
    }
}];
2

There are 2 answers

8
benbarth On BEST ANSWER

I'm answering my own question in hopes that it will help a few people.

I can't find this documented anywhere, but it looks like you have to use uploadTaskWithRequest:fromFile:progress:completionHandler: when using a background session configuration.

Here is a simple example:

AppDelegate *appDelegate = (AppDelegate *) [[UIApplication sharedApplication] delegate];

id config = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:@"com.opentext.core.uploadservice"];
id session = [NSURLSession sessionWithConfiguration:config delegate:appDelegate delegateQueue:[NSOperationQueue new]];

OMGMultipartFormData *multipartFormData = [OMGMultipartFormData new];
//[multipartFormData addFile:data parameterName:@"file" filename:nil contentType:nil];
[multipartFormData addParameters:[uploadBundle getParameters]];

NSURLRequest *rq = [OMGHTTPURLRQ POST:[uploadBundle.uploadUrl absoluteString] :multipartFormData];

id path = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"upload.NSData"];
[rq.HTTPBody writeToFile:path atomically:YES];

[[session uploadTaskWithRequest:rq fromFile:[NSURL fileURLWithPath:path]] resume];
2
tuttu47 On

The issues seems to be that you are trying to use a "streamed request" instead of file. The code works with

NSURLSessionUploadTask *uploadTask = [manager uploadTaskWithRequest:request fromFile:[NSURL fileURLWithPath:filePath] progress:&progress completionHandler:^(NSURLResponse *response, id responseObject, NSError *error){

.... }];

method without any issues. I also found that if you try to use the file data instead of the actual file (with uploadTaskWithRequest: fromData: progress: completionHandler:), the upload fails as explained in Upload tasks from NSData are not supported in background sessions