Front-End Web & Mobile
S3TransferManager for iOS – Part II: Asynchronous Uploads Best Practices
Version 2 of the AWS Mobile SDK
- This article and sample apply to Version 1 of the AWS Mobile SDK. If you are building new apps, we recommend you use Version 2. For details, please visit the AWS Mobile SDK page.
- This content is being maintained for historical reference.
This post demonstrates some best practices for achieving the high performance and avoiding common mistakes when using asynchronous uploads with S3TransferManager
.
Delegate methods may receive different subclasses of AmazonServiceRequest and AmazonServiceResponse
Because S3TransferManager
uses multipart uploads for large data and put object requests for small data, the delegate methods return different subclasses of AmazonServiceRequest
and AmazonServiceResponse
for each case. For example:
-
request:didSendData:totalBytesWritten:totalBytesExpectedToWrite:
returns-
S3PutObjectRequest
for small data -
S3UploadPartRequest
for large data
-
-
request:didCompleteWithResponse:
returns-
S3PutObjectResponse
for small data -
S3CompleteMultipartUploadResponse
for large data.
-
When you need to retrieve properties from the subclass, do not indiscriminately cast the AmazonServiceRequest
or AmazonServiceResponse
object to the subclass.
-(void)request:(AmazonServiceRequest *)request didSendData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite { // Do not do this! S3UploadPartRequest *uploadPartReqeust = (S3UploadPartRequest *)request; NSInteger partNumber = uploadPartReqeust.partNumber; NSLog(@"Do something with %d", partNumber); }
Instead, call respondsToSelector:
first to make sure that the request or response can respond to the selector, and then call performSelector:
:
-(void)request:(AmazonServiceRequest *)request didSendData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite { if([request respondsToSelector:@selector(partNumber)]) { NSInteger partNumber = [request performSelector:@selector(partNumber)]; NSLog(@"Do something with %d", partNumber); } }
Alternatively, you can use isKindOfClass:
or isMemberOfClass:
as follows:
-(void)request:(AmazonServiceRequest *)request didSendData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite { if([request isKindOfClass:[S3UploadPartRequest class]]) { S3UploadPartRequest *uploadPartReqeust = (S3UploadPartRequest *)request; NSInteger partNumber = uploadPartReqeust.partNumber; NSLog(@"Do something with %d", partNumber); } }
Use requestTag to uniquely identify a request
Although it is possible to uniquely identify a request with bucket and key names in many situations, it doesn’t always guarantee the uniqueness of a request. As mentioned in the previous post, it is often beneficial to use requestTag
to uniquely identify request.
putObjectRequest.requestTag = @"your-unique-tag"; [self.tm upload:putObjectRequest];
The requestTag
property is a free format NSString
, so you can pick any naming strategies that make sense for your apps.
-(void)request:(AmazonServiceRequest *)request didSendData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite { if([request.requestTag isEqualToString:@"your-unique-tag"]) { // Update the progress bar for "your-unique-tag". } }
Each thread should have its own instance of S3TransferManager
We recommend that you create an S3TransferManager
object for each thread, and do not share that object among multiple threads. It makes your code simpler by eliminating lots of thread controlling logic.
The operationQueue
property of S3TransferManager
is static, and is shared among all instances of S3TransferManager
objects. This ensures that the maxConcurrentOperationCount
is imposed among all instances of S3TransferManager
. When updating any properties of operationQueue
or calling any methods on it, please keep in mind that other threads may be affected by the changes even though NSOperationQueue
itself is thread-safe.
Delegate of S3TransferManager will be shared
Whenever you set the delegate
to S3TransferManager
and do not set the delegate
of S3PutObjectRequest
, the S3TransferManager
‘s delegate
will be used. It is possible that multiple requests share the same delegate
.
Avoid resetting the delegate
property of S3TransferManager
as much as possible once set because it may affect other requests running on the same S3TransferManager
.
Though it’s not encouraged, when you have to use S3TransferManager
from multiple classes or threads, consider using the delegate
property of S3PubObjectRequest
because it is unique to each request, and has minimal side effects on other requests.
Do not block the main thread
The AmazonServiceRequestDelegate
methods are always called on the main thread. It is safe to update UIs on the delegate methods, but never call blocking methods on them. Take a look at this post for discussion on why you should never block the main thread.
Best practices for using AmazonServiceRequestDelegate apply
Because asynchronous S3TransferManager
operations use AmazonServiceRequestDelegate
, these best practices apply.
Please let us know if you have any questions.
Further reading
We are hiring
If you like building mobile applications that use cloud services that our customers use on a daily basis, perhaps you would like to join the AWS Mobile SDK and Tools team. We are hiring Software Developers, Web Developers, and Product Managers.