Amazon S3 TransferUtility for iOS

On this page:

Amazon Simple Storage Service (S3)

Amazon Simple Storage Service (S3) provides secure, durable, highly scalable object storage in the cloud. Using the AWS Mobile SDK for iOS, you can directly access Amazon S3 from your mobile app. For information about Amazon S3 regional availability, see AWS Service Region Availability.

TransferUtility Features

In addition to downloading, uploading, pausing, resuming and cancelling transfers, use the Amazon S3 TransferUtility class to transfer data to a file without saving a source file. This class also completes transfers in the background, even if the system suspends your app. User choice to suspend an app cancels in-progress transfers.

Should I Use TransferManager or TransferUtility?

To choose which API best suits your needs, see Should I Use TransferManager or TransferUtility?

Setup#

To set your project up to use TransferUtility, take the steps below.

1. Setup the SDK, Credentials, and Services#

Follow the steps described in Amazon S3 Integration Setup to install the AWS Mobile SDK for iOS and configure AWS services, credentials, and permissions.

2. Import the SDK Amazon S3 APIs#

Add the following import statements to your Xcode project.

Swift
import AWSS3
Objective-C
#import <AWSS3/AWSS3.h>

3. Configure the Application Delegate#

The Transfer Utility for iOS uses the background transfer feature in iOS to continue data transfers even when your app isn't running.

Call the following method in - application:handleEventsForBackgroundURLSession: completionHandler: of your application delegate. When the app in the foreground, the delegate enables iOS to notify TransferUtility that a transfer has completed.

Swift
func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) {
    // Store the completion handler.
    AWSS3TransferUtility.interceptApplication(application, handleEventsForBackgroundURLSession: identifier, completionHandler: completionHandler)
}
Objective-C
- (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier
completionHandler:(void (^)())completionHandler {
    /* Store the completion handler.*/
    [AWSS3TransferUtility interceptApplication:application handleEventsForBackgroundURLSession:identifier completionHandler:completionHandler];
}

Uploading a File#

The following code for uploading a file by calling uploadFile: on AWSS3TransferUtility uses the pattern that is common to all the types of transfers TransferUtility supports. For code examples for other kinds of transfer, see More Transfer Examples. .

Swift
let fileURL = // The file to upload
let  transferUtility = AWSS3TransferUtility.default()
transferUtility.uploadFile(fileURL,
        bucket: S3BucketName,
        key: S3UploadKeyName,
        contentType: "image/png",
        expression: nil,
        completionHandler: nil).continueWith {
    (task) -> AnyObject! in if let error = task.error {
        print("Error: \(error.localizedDescription)")
    }

    if let _ = task.result {
        // Do something with uploadTask.
    }
    return nil;
}
Objective-C
NSURL *fileURL = // The file to upload.

AWSS3TransferUtility *transferUtility = [AWSS3TransferUtility defaultS3TransferUtility];
[[transferUtility uploadFile:fileURL
                    bucket:@"YourBucketName"
                    key:@"YourObjectKeyName"
                    contentType:@"text/plain"
                    expression:nil
            completionHander:nil] continueWithBlock:^id(AWSTask *task) {
    if (task.error) {
        NSLog(@"Error: %@", task.error);
    }
    if (task.result) {
        AWSS3TransferUtilityUploadTask *uploadTask = task.result;
        // Do something with uploadTask.
    }

    return nil;
}];

Tracking Progress and Completion#

Implement progress and completion actions for TransferManager transfers by passing progressBlock and completionHandler blocks to the call to TransferUtility that initiates the transfer.

The following example of initiating a data upload shows how progress and completion handling is typically done for all transfers.

Swift
let data = // The data to upload

let expression = AWSS3TransferUtilityUploadExpression()
expression.progressBlock = {(task, progress) in DispatchQueue.main.async(execute: {
        // Do something e.g. Update a progress bar.
    })
}

let completionHandler = { (task, error) -> Void in
    DispatchQueue.main.async(execute: {
        // Do something e.g. Alert a user for transfer completion.
        // On failed uploads, `error` contains the error object.
    })
}

let  transferUtility = AWSS3TransferUtility.default()

transferUtility.uploadData(data,
            bucket: S3BucketName,
            key: S3UploadKeyName,
            contentType: "image/png",
            expression: expression,
            completionHandler: completionHandler).continueWith { (task) -> AnyObject! in
    if let error = task.error {
        print("Error: \(error.localizedDescription)")
    }

    if let _ = task.result {
        // Do something with uploadTask.
    }

    return nil;
}
Objective-C
NSData *dataToUpload = // The data to upload.

AWSS3TransferUtilityUploadExpression *expression = [AWSS3TransferUtilityUploadExpression new];
expression.progressBlock = ^(AWSS3TransferUtilityTask *task, NSProgress *progress) {
    dispatch_async(dispatch_get_main_queue(), ^{
        // Do something e.g. Update a progress bar.
    });
};


AWSS3TransferUtilityUploadCompletionHandlerBlock completionHandler = ^(AWSS3TransferUtilityUploadTask *task, NSError *error) {
    dispatch_async(dispatch_get_main_queue(), ^{
        // Do something e.g. Alert a user for transfer completion.
        // On failed uploads, `error` contains the error object.
    });
};

AWSS3TransferUtility *transferUtility = [AWSS3TransferUtility defaultS3TransferUtility];
[[transferUtility uploadData:dataToUpload
                bucket:@"YourBucketName"
                key:@"YourObjectKeyName"
                contentType:@"text/plain"
                expression:expression
        completionHander:completionHandler] continueWithBlock:^id(AWSTask *task) {
    if (task.error) {
        NSLog(@"Error: %@", task.error);
    }
    if (task.result) {
       AWSS3TransferUtilityUploadTask *uploadTask = task.result;
        // Do something with uploadTask.
    }

    return nil;
}];

Managing Transfers#

This section describes how to manage TransferUtility transfers at different points in the app lifecycle.

With the App in the Foreground#

To suspend, resume, and cancel uploads and downloads, retain references to AWSS3TransferUtilityUploadTask and AWSS3TransferUtilityDownloadTask. To manage data transfers call suspend, resume, and cancel on those tasks. The following example shows the cancel method being called on an upload.

Swift
transferUtility.uploadFile(fileURL,
        bucket: S3BucketName,
        key: S3UploadKeyName,
        contentType: "image/png",
        expression: nil,
        completionHandler: nil).continueWith {
    (task) -> AnyObject! in if let error = task.error {
        print("Error: \(error.localizedDescription)")
    }

    if let uploadTask = task.result {
        uploadTask.cancel()
    }
    return nil;
}
Objective-C
[[transferUtility uploadFile:fileURL
                    bucket:@"YourBucketName"
                    key:@"YourObjectKeyName"
                    contentType:@"text/plain"
                    expression:nil
            completionHander:nil] continueWithBlock:^id(AWSTask *task) {
    if (task.error) {
        NSLog(@"Error: %@", task.error);
    }
    if (task.result) {
        AWSS3TransferUtilityUploadTask *uploadTask = task.result;
        [uploadTask cancel]
    }

    return nil;
}];

When a Suspended App Returns to the Foreground#

Upon App Returning to the Foreground

When an app that has initiated a TransferUtility transfer becomes suspended and then returns to the foreground, the transfer may still be in progress or may have completed. In both cases, use the following code to reestablish the transfer as being handled by progress and completion blocks of the app in the foreground.

The code uses downloading a file as the example but the pattern also works for upload:

You receive AWSS3TransferUtilityUploadTask and AWSS3TransferUtilityDownloadTask when you initiate the upload and download respectively. These tasks have a property called taskIdentifier, which uniquely identifies the transfer task object within the Transfer Utility. Your app should persist the identifier through closure and relaunch, so that you can uniquely identify the task objects when the app is comes back into the foreground.

Swift
override func viewDidLoad() {
super.viewDidLoad()

...

let transferUtility = AWSS3TransferUtility.default()

var uploadProgressBlock: AWSS3TransferUtilityProgressBlock? = {(task: AWSS3TransferUtilityTask, progress: Progress) in
    DispatchQueue.main.async {
        // Handle progress feedback, e.g. update progress bar
    }
}
var downloadProgressBlock: AWSS3TransferUtilityProgressBlock? = {
    (task: AWSS3TransferUtilityTask, progress: Progress) in DispatchQueue.main.async {
        // Handle progress feedback, e.g. update progress bar
    }
}
var completionBlockUpload:AWSS3TransferUtilityUploadCompletionHandlerBlock? = {
    (task, error) in DispatchQueue.main.async {
        // perform some action on completed upload operation
    }
}
var completionBlockDownload:AWSS3TransferUtilityDownloadCompletionHandlerBlock? = {
    (task, url, data, error) in DispatchQueue.main.async {
        // perform some action on completed download operation
    }
}

transferUtility.enumerateToAssignBlocks(forUploadTask: {
    (task, progress, completion) -> Void in

        let progressPointer = AutoreleasingUnsafeMutablePointer<AWSS3TransferUtilityProgressBlock?>(& uploadProgressBlock)

        let completionPointer = AutoreleasingUnsafeMutablePointer<AWSS3TransferUtilityUploadCompletionHandlerBlock?>(&completionBlockUpload)

        // Reassign your progress feedback
        progress?.pointee = progressPointer.pointee

        // Reassign your completion handler.
        completion?.pointee = completionPointer.pointee

}, downloadTask: {
    (task, progress, completion) -> Void in

        let progressPointer = AutoreleasingUnsafeMutablePointer<AWSS3TransferUtilityProgressBlock?>(&downloadProgressBlock)

        let completionPointer = AutoreleasingUnsafeMutablePointer<AWSS3TransferUtilityDownloadCompletionHandlerBlock?>(&completionBlockDownload)

        // Reassign your progress feedback
        progress?.pointee = progressPointer.pointee

        // Reassign your completion handler.
        completion?.pointee = completionPointer.pointee
})

 if let downloadTask = task.result {
    // Do something with downloadTask.
}
Objective-C
- (void)viewDidLoad {
    [super viewDidLoad];

    ...

    AWSS3TransferUtility *transferUtility = [AWSS3TransferUtility defaultS3TransferUtility];
    [transferUtility enumerateToAssignBlocksForUploadTask:^(
        AWSS3TransferUtilityUploadTask *uploadTask,
        __autoreleasing AWSS3TransferUtilityUploadProgressBlock *uploadProgressBlockReference,
        __autoreleasing AWSS3TransferUtilityUploadCompletionHandlerBlock *completionHandlerReference
    ) {
        NSLog(@"%lu", (unsigned long)uploadTask.taskIdentifier);

        // Use `uploadTask.taskIdentifier` to determine what blocks to assign.

        *uploadProgressBlockReference = ...; // Reassign your progress feedback block.
        *completionHandlerReference = ...; // Reassign your completion handler.
    }
    downloadTask:^(AWSS3TransferUtilityDownloadTask *downloadTask, __autoreleasing AWSS3TransferUtilityDownloadProgressBlock *downloadProgressBlockReference, __autoreleasing AWSS3TransferUtilityDownloadCompletionHandlerBlock *completionHandlerReference) {
        NSLog(@"%lu", (unsigned long)downloadTask.taskIdentifier);

            // Use `downloadTask.taskIdentifier` to determine what blocks to assign.
       *downloadProgressBlockReference =  // Reassign your progress feedback block.
       *completionHandlerReference = // Reassign your completion handler.
    }];
}

if (task.result) {
    AWSS3TransferUtilityUploadTask *downloadTask = task.result;
    // Do something with downloadTask.
}

More Transfer Examples#

This section provides descriptions and abbreviated examples of the aspects of each type of transfer that are unique. For information about typical code surrounding the following snippets see Managing Transfers and Tracking Progress and Completion.

Downloading to a File#

The following code shows how to download a file.

Swift
let fileURL = // The file URL of the download destination.

// Add progress and completion blocks
. . .

let  transferUtility = AWSS3TransferUtility.default()
transferUtility.download(
        to: fileURL
        bucket: S3BucketName,
        key: S3DownloadKeyName,
        expression: expression,
        completionHander: completionHandler
).continueWith {
    (task) -> AnyObject! in if let error = task.error {
        print("Error: \(error.localizedDescription)")
    }

    if let _ = task.result {
        // Do something with downloadTask.
    }
    return nil;
}
Objective-C
NSURL *fileURL = ...; // The file URL of the download destination.

// Add progress and completion blocks
. . .

AWSS3TransferUtility *transferUtility = [AWSS3TransferUtility defaultS3TransferUtility];
[[transferUtility downloadToURL:nil
                bucket:S3BucketName
                key:S3DownloadKeyName
                expression:expression
        completionHander:completionHandler] continueWithBlock:^id(AWSTask *task) {
    if (task.error) {
        NSLog(@"Error: %@", task.error);
    }
    if (task.result) {
        AWSS3TransferUtilityDownloadTask *downloadTask = task.result;
        // Do something with downloadTask.
    }

    return nil;
}];

Uploading Binary Data to a File#

To upload data to a file in Amazon S3 call uploadData:.

This method saves the data as a file in a temporary directory. The next time AWSS3TransferUtility is initialized, the expired temporary files are cleaned up. If you upload many large objects to an Amazon S3 bucket in a short period of time, it is more efficient to use the upload file method and then manually purge the unnecessary temporary files as early as possible.

Swift
let data = // The data to upload

// Add progress and completion blocks
. . .

let  transferUtility = AWSS3TransferUtility.default()

transferUtility.uploadData(data,
            bucket: S3BucketName,
            key: S3UploadKeyName,
            contentType: "image/png",
            expression: expression,
            completionHandler: completionHandler).continueWith { (task) -> AnyObject! in
    if let error = task.error {
        print("Error: \(error.localizedDescription)")
    }

    if let _ = task.result {
        // Do something with uploadTask.
    }

    return nil;
}
Objective-C
NSData *dataToUpload = // The data to upload.

// Add progress and completion blocks
. . .

AWSS3TransferUtility *transferUtility = [AWSS3TransferUtility defaultS3TransferUtility];
[[transferUtility uploadData:dataToUpload
                bucket:@"YourBucketName"
                key:@"YourObjectKeyName"
                contentType:@"text/plain"
                expression:expression
        completionHander:completionHandler] continueWithBlock:^id(AWSTask *task) {
    if (task.error) {
        NSLog(@"Error: %@", task.error);
    }
    if (task.result) {
        AWSS3TransferUtilityUploadTask *uploadTask = task.result;
        // Do something with uploadTask.
    }

    return nil;
}];

Downloading Binary Data to a File#

The following code shows how to download binary a file.

Swift
let fileURL = // The file URL of the download destination.

// Add progress and completion blocks
. . .

let  transferUtility = AWSS3TransferUtility.default()
transferUtility.downloadData(
        fromBucket: S3BucketName,
        key: S3DownloadKeyName,
        expression: expression,
        completionHander: completionHandler
).continueWith {
    (task) -> AnyObject! in if let error = task.error {
        print("Error: \(error.localizedDescription)")
    }

    if let _ = task.result {
        // Do something with downloadTask.
    }

    return nil;
}
Objective-C
AWSS3TransferUtilityDownloadExpression *expression = [AWSS3TransferUtilityDownloadExpression new];

// Add progress and completion blocks
. . .

AWSS3TransferUtility *transferUtility = [AWSS3TransferUtility defaultS3TransferUtility];
[[transferUtility downloadDataFromBucket:S3BucketName
    key:S3DownloadKeyName
    expression:expression
    completionHander:completionHandler] continueWithBlock:^id(AWSTask *task) {
        if (task.error) {
            NSLog(@"Error: %@", task.error);
        }
        if (task.result) {
            AWSS3TransferUtilityDownloadTask *downloadTask = task.result;
            // Do something with downloadTask.
        }

        return nil;
    }
];

Limitations#

The S3 Transfer Utility generates Amazon S3 pre-signed URLs to use for background data transfer. Using Amazon Cognito Identity, you receive AWS temporary credentials. The credentials are valid for up to 60 minutes. At the same time, generated S3 pre-signed URLs cannot last longer than that time. Because of this limitation, the Amazon S3 Transfer Utility enforces 50 minute transfer timeouts, leaving a 10 minute buffer before AWS temporary credentials are regenerated. After 50 minutes, you receive a transfer failure.

If you need to transfer data that cannot be transferred in under 50 minutes, use AWSS3 instead.