AWS Mobile Blog

Using the AWS SDK for iOS Asynchronously – Part II: AmazonRequestDelegate Best Practices

by Yosuke Matsuda | on | in S3 | Permalink | Comments |  Share

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.

In my previous post, I demonstrated the difference between synchronous and asynchronous calls in the AWS SDK for iOS. This post will show some best practices for avoiding common mistakes when using the default asynchronous method in the SDK.

Avoid using AmazonServiceRequestDelegate in a background thread

If you do the following, your delegate methods will not be called, and the app will never get the response.

    ...
    [self performSelectorInBackground:@selector(upload) withObject:nil];
}

- (void)upload
{
    S3PutObjectRequest *request = [[S3PutObjectRequest alloc] initWithKey:objectKey inBucket:bucketName];
    request.data = data;
    request.delegate = self;

    [s3Client putObject:request];
}

Background threads have their own run loop, separate from the one on the main thread. When you make an asynchronous request using AmazonServiceRequestDelegate, the delegate will be registered with the background thread’s run loop that is responsible for calling the delegate methods.

However, putObject: immediately returns and upload also returns before the client had a chance to process the request. When upload finishes its execution, the background thread will disappear with its run loop, and nothing that is responsible for calling the delegate methods will be left behind. That’s why you will never get the result back with the above sample code.

Retain a strong reference to the client

Use properties

Retaining a strong reference to the client ensures that the client, request and response objects are not released while the request is still processing. One way to do this is to make the client object a property.

When using ARC

Make sure to keep a strong reference to the client until the request completes.

@property (nonatomic, strong) AmazonS3Client *s3;

When not using ARC

Make sure to retain the client object until the request completes.

@property (nonatomic, retain) AmazonS3Client *s3;

Use a client manager class

Another, and in many cases more preferable option is to create a class that is responsible for managing client objects. Here’s a simplified example.

@interface YourClientManager : NSObject
{}

+ (YourClientManager *)sharedClientManager;
- (AmazonS3Client *)s3;

@end

static YourClientManager *sharedClientManager = nil;
static AmazonS3Client *s3 = nil;

@implementation YourClientManager

+ (YourClientManager *)sharedClientManager
{
    if(sharedClientManager == nil)
    {
        @synchronized(self)
        {
            if(sharedClientManager == nil)
            {
                sharedClientManager = [YourClientManager new];
            }
        }
    }

    return sharedClientManager;
}

- (AmazonS3Client *)s3
{
    if(s3 == nil)
    {
        @synchronized(self)
        {
            if(s3 == nil)
            {
                // Note that this is not the preferred way to create the AmazonCredentials object. Do not ship apps with your credentials in them.
                AmazonCredentials *credentials = [[AmazonCredentials alloc] initWithAccessKey:accessKey withSecretKey:secretKey];

                s3 = [[AmazonS3Client alloc] initWithCredentials:credentials];
            }
        }
    }

    return s3;
}

Our sample apps with token vending machine have a class called AmazonClientManager. This class always keeps the client objects and ensures they are not released prematurely. Please take a look at one of these samples to see if your app can incorporate this design pattern.

Retain a strong reference to the delegate

The delegate is an assign property of request objects. This means request doesn’t retain self when request.delegate = self is called. You need to make sure self is retained until all of the delegate methods are called. As a general design rule, the owner of the request is responsible for retaining the delegate, too. This helps you avoid circular dependencies of classes. So, you need to make sure the delegate will be retained by its controller object.

With these points in mind, it should be fairly straightforward to use AmazonServiceRequestDelegate in your app. Please let us know if you have any questions.

Further reading: