Skywalker
Skywalker

Reputation: 1586

Amazon S3 iOS SDK background upload with progress block

I am working on an app that depends on AWS for file uploads and downloads. In the case of uploads, when I upload video files using AWSS3TransferManagerUploadRequest, I can show progress bar using progress block, but when I press home button and app enters background, the upload pauses (probably because it uses NSURLConnection). Only if the app is running in foreground will upload take place.

This is the code for uploading files using AWSS3TransferManagerUploadRequest.

AWSS3TransferManager *transferManager = [AWSS3TransferManager defaultS3TransferManager];

AWSS3TransferManagerUploadRequest *uploadRequest = [AWSS3TransferManagerUploadRequest new];

uploadRequest.bucket = bucketName;
uploadRequest.key = appropriateKey;

uploadRequest.body = [NSURL fileURLWithPath:filePath];

uploadRequest.contentType = contentType;

uploadRequest.uploadProgress = progressBlock;


[[transferManager upload:uploadRequest] continueWithExecutor:[AWSExecutor mainThreadExecutor] withBlock:^id(AWSTask *task) {
    if (task.error)
    {
        if ([task.error.domain isEqualToString:AWSS3TransferManagerErrorDomain])
        {
            switch (task.error.code)
            {
                case AWSS3TransferManagerErrorCancelled:
                case AWSS3TransferManagerErrorPaused:
                    break;

                default:
                    failBlock(task.error.code, task.error.localizedDescription);
                    break;
            }
        }
        else
        {
            failBlock(task.error.code, task.error.localizedDescription);
        }
    }
    if (task.result)
    {
        AWSS3TransferManagerUploadOutput *uploadOutput = task.result;
        successBlock(uploadOutput);
    }
    return nil;
}];

This works, except for transfer in background.

So, I went ahead and used the AWS's pre-signed URLs. The following is the code for the same.

AWSS3GetPreSignedURLRequest *getPreSignedURLRequest = [AWSS3GetPreSignedURLRequest new];
getPreSignedURLRequest.bucket = bucketName;

getPreSignedURLRequest.key = appropriateKey;
getPreSignedURLRequest.HTTPMethod = AWSHTTPMethodPUT;
getPreSignedURLRequest.expires = [NSDate dateWithTimeIntervalSinceNow:3600];


//Important: set contentType for a PUT request.
NSString *fileContentTypeStr = contentType;
getPreSignedURLRequest.contentType = fileContentTypeStr;
[[[AWSS3PreSignedURLBuilder defaultS3PreSignedURLBuilder] getPreSignedURL:getPreSignedURLRequest]
 continueWithBlock:^id(AWSTask *task) {

     if (task.error) {
         NSLog(@"Error: %@",task.error);
     } else {

         NSURL *presignedURL = task.result;
         NSLog(@"upload presignedURL is: \n%@", presignedURL);

         NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:presignedURL];
         request.cachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
         [request setHTTPMethod:@"PUT"];
         [request setValue:fileContentTypeStr forHTTPHeaderField:@"Content-Type"];


         NSURLSession *session;
         NSURL *uploadFileUrl = [NSURL fileURLWithPath:filePath];
         NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request fromFile:uploadFileUrl];
         //uploadTask is an instance of NSURLSessionDownloadTask.
         //session is an instance of NSURLSession.
         [uploadTask resume];

     }

     return nil;
 }];

This solves the problem of uploading in the background. But it does not have a progress block and because of that I cannot track progress.

I have to be able to track progress and upload from background. Is this possible? Or is there any workaround?

Upvotes: 5

Views: 5834

Answers (2)

Andrew Longhorn
Andrew Longhorn

Reputation: 1

Sorry, but this is fundamentally not supported using core iOS background upload capabilities.

By design, the NSURLSession background upload will take 100s of parallel chunks and only return success/fail as an atomic response generally. The actual progress is opaque to the app.

You can't really get around this, the more you serialise, the more you trigger "next part" uploads which will quickly incur the wrath of the rate limiting side of that.

Background upload is specifically designed to be minimal touch to save battery and 3G data usage. It's not designed for anything interactive.

I wonder, given the need to use background upload, presumably for large assets, what is the actual need to show the user progress that may be very slow? Isn't success/fail enough?

Maybe to solve this you could run the uploads in the foreground if absolutely necessary, then you will have the feedback, if the user gets sick of waiting or the phone sleeps, then in the app teardown call-back, switch to resuming on a background task as the user is no longer interested in watching the progress bar?

Upvotes: 0

Skywalker
Skywalker

Reputation: 1586

So, I did a lot of digging around and found an answer for this. It may be helpful for someone.

The AWSS3TransferManager has progress block to track progress but no support for background transfer. And AWSS3GetPreSignedURLRequest has support for background transfer but no way to track progress.

They have another service via which you can track progress and upload/download in the background. Using AWSS3TransferUtility, you can do both these things. Do note that this is still in beta.

Refer the docs for more info - http://docs.aws.amazon.com/mobile/sdkforios/developerguide/s3transferutility.html

I had a lot of trouble with this(partly due to my ignorance). I hope this is useful for anyone with similar problems.

Upvotes: 4

Related Questions