Storing User Preference in Amazon DynamoDB using the Mobile SDKs

Articles & Tutorials>Java>Storing User Preference in Amazon DynamoDB using the Mobile SDKs
Storing User Preference in Amazon DynamoDB using the AWS SDK for iOS and the AWS SDK for Android.

Details

Submitted By: Yosuke@AWS
AWS Products Used: AWS SDK for Android, AWS SDK for iOS, Amazon DynamoDB
Language(s): Java, Objective-C
Created On: January 24, 2012 8:46 PM GMT
Last Updated: October 22, 2013 3:53 PM GMT

Storing User Preference in Amazon DynamoDB using the AWS SDK for iOS and the AWS SDK for Android

Amazon DynamoDB is a fast, highly scalable, highly available, cost-effective, non-relational database service. Amazon DynamoDB removes traditional scalability limitations on data storage while maintaining low latency and predictable performance. The sample mobile application described here demonstrates how to store user preferences in Amazon DynamoDB. Because more and more people are using multiple mobile devices, connecting these devices to the cloud and storing user preferences in the cloud enables developers to provide a more uniform cross-device experience for their users.

This article shows sample code for both the iOS and Android platforms. The complete sample code and project files are included in the AWS SDKs for these mobile platforms. Links to the SDKs are available at the end of this article.

To use the sample app, you'll need to deploy a token vending machine (TVM). A TVM is a cloud-based application that manages AWS credentials for users of mobile applications. To deploy the TVM, you'll first need to obtain your own AWS credentials: an Access Key ID and Secret Key.

If you haven't already signed up for Amazon Web Services (AWS), you will need to do that first to get your AWS credentials. You can sign up for AWS here. After you sign up, you can retrieve your credentials at this page. The credentials will be used to set up the TVM to authenticate users of AWS mobile applications. Sample Java web applications are available here: Anonymous TVM and Identity TVM (This sample uses Anonymous TVM).

Overview

In Amazon DynamoDB, a database is a collection of tables. A table is a collection of items, and each item is a collection of attributes. For our app, we create a single table to store our list of users and their preferences. Each item in the table represents an individual user. Each item has multiple attributes, which include the user's name and their preferences. Each item also has a hash key�in this case, userNo�which is the primary key for the table.

The app demonstrates how to add and remove users, and modify and retrieve their preference data. The app also demonstrates how to create and delete Amazon DynamoDB tables.

Registering the Device with Token Vending Machine

In order to create an Amazon DynamoDB client, we must first register the mobile device with the token vending machine (TVM). For this sample, we use the Anonymous TVM to register the device. Then we store the UID and key returned by the TVM on the device.

  • iOS

RegisterDeviceRequest *request = [[[RegisterDeviceRequest alloc] initWithEndpoint:self.endpoint 
                                                                           andUid:uid 
                                                                           andKey:key 
                                                                         usingSSL:self.useSSL] autorelease];
ResponseHandler *handler = [[[ResponseHandler alloc] init] autorelease];

response = [self processRequest:request responseHandler:handler];
if ( [response wasSuccessful]) {
    [AmazonKeyChainWrapper registerDeviceId:uid andKey:key];
}

  • Android

RegisterDeviceRequest registerDeviceRequest = new RegisterDeviceRequest(this.endpoint, this.useSSL, uid, key);
ResponseHandler handler = new ResponseHandler();

response = this.processRequest(registerDeviceRequest, handler);
if (response.requestWasSuccessful()) {
	AmazonSharedPreferencesWrapper.registerDeviceId(this.sharedPreferences, uid, key);
}
		    

Retrieving the Temporary Credentials from Token Vending Machine

The following code demonstrates how to request that the TVM generate temporary credentials, and how to store the returned credentials on the device.

  • iOS

Request *request = [[[GetTokenRequest alloc] initWithEndpoint:self.endpoint 
                                                       andUid:uid andKey:key usingSSL:self.useSSL] autorelease];
ResponseHandler *handler = [[[GetTokenResponseHandler alloc] initWithKey:key] autorelease];

GetTokenResponse *response = (GetTokenResponse *)[self processRequest:request responseHandler:handler];

if ( [response wasSuccessful]) {
    [AmazonKeyChainWrapper storeCredentialsInKeyChain:response.accessKey 
                                            secretKey:response.secretKey 
                                        securityToken:response.securityToken 
                                           expiration:response.expirationDate];
}

  • Android

Request getTokenRequest = new GetTokenRequest(this.endpoint,this.useSSL, uid, key);
ResponseHandler handler = new GetTokenResponseHandler(key);

GetTokenResponse getTokenResponse = (GetTokenResponse) this.processRequest(getTokenRequest, handler);
if (getTokenResponse.requestWasSuccessful()) {
	AmazonSharedPreferencesWrapper.storeCredentialsInSharedPreferences(
			this.sharedPreferences, 
                        getTokenResponse.getAccessKey(),
			getTokenResponse.getSecretKey(),
			getTokenResponse.getSecurityToken(),
			getTokenResponse.getExpirationDate());
}
		    

Creating an Amazon DynamoDB Client

To make service requests to Amazon DynamoDB, you need to instantiate an Amazon DynamoDB client. The code below shows how to create an Amazon DynamoDB client for iOS or Android using the stored temporary credentials from the TVM.

  • iOS

AmazonCredentials *credentials = [AmazonKeyChainWrapper getCredentialsFromKeyChain];
AmazonDynamoDBClient *ddb = [[AmazonDynamoDBClient alloc] initWithCredentials:credentials];

  • Android

AWSCredentials credentials = AmazonSharedPreferencesWrapper.getCredentialsFromSharedPreferences(this.sharedPreferences);

AmazonDynamoDBClient ddb = new AmazonDynamoDBClient(credentials);
		    

Creating a User List (Table Creation)

Each user's preferences are stored as items in an Amazon DynamoDB table. The following code creates that table using the client we created above. Every Amazon DynamoDB table require a hash key. In this sample, we use userNo as the hash key for the table.

  • iOS

DynamoDBCreateTableRequest *createTableRequest = [[DynamoDBCreateTableRequest new] autorelease];

DynamoDBProvisionedThroughput *provisionedThroughput = [[DynamoDBProvisionedThroughput new] autorelease];
provisionedThroughput.readCapacityUnits  = [NSNumber numberWithInt:10];
provisionedThroughput.writeCapacityUnits = [NSNumber numberWithInt:5];

DynamoDBKeySchemaElement *keySchemaElement = [[[DynamoDBKeySchemaElement alloc] initWithAttributeName:TEST_TABLE_HASH_KEY
                                                                                           andKeyType:@"HASH"] autorelease];
DynamoDBAttributeDefinition *attributeDefinition = [[DynamoDBAttributeDefinition new] autorelease];
attributeDefinition.attributeName = TEST_TABLE_HASH_KEY;
attributeDefinition.attributeType = @"N";

createTableRequest.tableName = TEST_TABLE_NAME;
createTableRequest.provisionedThroughput = provisionedThroughput;
[createTableRequest addKeySchema:keySchemaElement];
[createTableRequest addAttributeDefinition:attributeDefinition];

DynamoDBCreateTableResponse *response = [[AmazonClientManager ddb] createTable:createTableRequest];

  • Android

AmazonDynamoDBClient ddb = UserPreferenceDemoActivity.clientManager.ddb();

KeySchemaElement kse = new KeySchemaElement().withAttributeName("userNo").withKeyType(KeyType.HASH);
AttributeDefinition ad = new AttributeDefinition().withAttributeName("userNo").withAttributeType(ScalarAttributeType.N);
ProvisionedThroughput pt = new ProvisionedThroughput().withReadCapacityUnits(10l).withWriteCapacityUnits(5l);

CreateTableRequest request = new CreateTableRequest()
        .withTableName(PropertyLoader.getInstance().getTestTableName())
        .withKeySchema(kse).withAttributeDefinitions(ad)
        .withProvisionedThroughput(pt);

ddb.createTable(request);

Checking the Status of the Table (Table Description)

Before we can move to the next step (creating users), we must wait until the status of the tables is ACTIVE. To retrieve the status of the table, we use a describe table request. This request returns information about the table such as the name of the table, item count, creation date and time, and its status.

  • iOS

DynamoDBDescribeTableRequest *request = [[[DynamoDBDescribeTableRequest alloc] initWithTableName:TEST_TABLE_NAME] autorelease];
DynamoDBDescribeTableResponse *response = [[AmazonClientManager ddb] describeTable:request];

NSString *status = response.table.tableStatus;
		    

  • Android

AmazonDynamoDBClient ddb = UserPreferenceDemoActivity.clientManager.ddb();

DescribeTableRequest request = new DescribeTableRequest().withTableName(PropertyLoader.getInstance().getTestTableName());
DescribeTableResult result = ddb.describeTable(request);

String status = result.getTable().getTableStatus();
		    

Creating Users (Item Creation)

For each user, we'll create an item in the table. An item is a collection of attribute/value pairs. For each item, we'll have three attributes: userNo, firstName and lastName. These are added to a put item request in order to create the item.

  • iOS

NSMutableDictionary *userDic =
[NSDictionary dictionaryWithObjectsAndKeys:
 [[[DynamoDBAttributeValue alloc] initWithN:[NSString stringWithFormat:@"%d", i]] autorelease], TEST_TABLE_HASH_KEY,
 [[[DynamoDBAttributeValue alloc] initWithS:[Constants getRandomName]] autorelease], @"firstName",
 [[[DynamoDBAttributeValue alloc] initWithS:[Constants getRandomName]] autorelease], @"lastName",
 nil];

DynamoDBPutItemRequest *request = [[[DynamoDBPutItemRequest alloc] initWithTableName:TEST_TABLE_NAME andItem:userDic] autorelease];
DynamoDBPutItemResponse *response = [[AmazonClientManager ddb] putItem:request];

  • Android

HashMap<String, AttributeValue> item = new HashMap<String, AttributeValue>();

AttributeValue userNo = new AttributeValue().withN(String.valueOf(i));
item.put("userNo", userNo);

AttributeValue firstName = new AttributeValue().withS(Constants.getRandomName());
item.put("firstName", firstName);

AttributeValue lastName = new AttributeValue().withS(Constants.getRandomName());
item.put("lastName", lastName);

PutItemRequest request = new PutItemRequest().withTableName(
        PropertyLoader.getInstance().getTestTableName()).withItem(item);

ddb.putItem(request);

Deleting Users (Item Deletion)

To remove a user from the list simply means deleting the corresponding item from the table. We specify the item we wish to delete using the hash key for the item.

  • iOS

DynamoDBDeleteItemRequest *deleteItemRequest = [[DynamoDBDeleteItemRequest new] autorelease];

deleteItemRequest.tableName = TEST_TABLE_NAME;
deleteItemRequest.key = [NSMutableDictionary dictionaryWithObject:aPrimaryKey
                                                           forKey:TEST_TABLE_HASH_KEY];

DynamoDBDeleteItemResponse *deleteItemResponse = [[AmazonClientManager ddb] deleteItem:deleteItemRequest];

  • Android

AmazonDynamoDBClient ddb = UserPreferenceDemoActivity.clientManager.ddb();

HashMap<String, AttributeValue> keyMap = new HashMap<String, AttributeValue>();
keyMap.put("userNo", targetValue);
DeleteItemRequest request = new DeleteItemRequest().withTableName(
		PropertyLoader.getInstance().getTestTableName()).withKey(keyMap);
ddb.deleteItem(request);

Listing Users (Table Scan)

We can retrieve a collection of users with a scan request. A scan request simply scans the table and returns the results in an undetermined order. Scan is an expensive operation and should be used with care to avoid disrupting your higher priority production traffic on the table. See the Amazon DynamoDB developer guide for more recommendations for safely using the Scan operation.

  • iOS

DynamoDBScanRequest *request = [[[DynamoDBScanRequest alloc] initWithTableName:TEST_TABLE_NAME] autorelease];
DynamoDBScanResponse *response = [[AmazonClientManager ddb] scan:request];

NSMutableArray *users = response.items;
		    

  • Android

AmazonDynamoDBClient ddb = UserPreferenceDemoActivity.clientManager.ddb();

ScanRequest request = new ScanRequest();
request.setTableName(PropertyLoader.getInstance().getTestTableName());
ScanResult result = ddb.scan(request);

ArrayList<HashMap<String, AttributeValue>> users = (ArrayList<HashMap<String, AttributeValue>>) result.getItems();

Retrieving a User's Preferences (Item Retrieval)

Knowing a user's userNo, the hash key of the table, it is easy to find the item for the user. This next snippet shows how to get all the attributes for an item using the hash key.

  • iOS

DynamoDBGetItemRequest *getItemRequest = [[DynamoDBGetItemRequest new] autorelease];

DynamoDBAttributeValue *attributeValue = [[[DynamoDBAttributeValue alloc] initWithN:[NSString stringWithFormat:@"%d", userNo]] autorelease];

getItemRequest.tableName = TEST_TABLE_NAME;
getItemRequest.key = [NSMutableDictionary dictionaryWithObject:attributeValue forKey:TEST_TABLE_HASH_KEY];

DynamoDBGetItemResponse *getItemResponse = [[AmazonClientManager ddb] getItem:getItemRequest];

NSMutableDictionary *userPreferences = getItemResponse.item;
		    

  • Android

AmazonDynamoDBClient ddb = UserPreferenceDemoActivity.clientManager.ddb();

AttributeValue userNoAttr = new AttributeValue().withN(String.valueOf(userNo));
HashMap<String, AttributeValue> keyMap = new HashMap<String, AttributeValue>();
keyMap.put("userNo", userNoAttr);
GetItemRequest request = new GetItemRequest().withTableName(
        PropertyLoader.getInstance().getTestTableName()).withKey(keyMap);

GetItemResult result = ddb.getItem(request);

HashMap<String, AttributeValue> userPreferences = (HashMap<String, AttributeValue>) result.getItem();

Modifying User Preferences (Item Update)

The hash key also makes it easy to update an attribute for an item.

  • iOS

DynamoDBUpdateItemRequest *updateItemRequest = [[DynamoDBUpdateItemRequest new] autorelease];

DynamoDBAttributeValue *attributeValue = [[[DynamoDBAttributeValue alloc] initWithS:aValue] autorelease];
DynamoDBAttributeValueUpdate *attributeValueUpdate = [[[DynamoDBAttributeValueUpdate alloc] initWithValue:attributeValue
                                                                                                andAction:@"PUT"] autorelease];

updateItemRequest.tableName = TEST_TABLE_NAME;
updateItemRequest.attributeUpdates = [NSMutableDictionary dictionaryWithObject:attributeValueUpdate
                                                                        forKey:aKey];
updateItemRequest.key = [NSMutableDictionary dictionaryWithObject:aPrimaryKey
                                                           forKey:TEST_TABLE_HASH_KEY];

DynamoDBUpdateItemResponse *updateItemResponse = [[AmazonClientManager ddb] updateItem:updateItemRequest];

  • Android

AmazonDynamoDBClient ddb = UserPreferenceDemoActivity.clientManager.ddb();

AttributeValue av = new AttributeValue().withS(value);
AttributeValueUpdate avu = new AttributeValueUpdate().withValue(av).withAction(AttributeAction.PUT);
HashMap<String, AttributeValue> keyMap = new HashMap<String, AttributeValue>();
keyMap.put("userNo", targetValue);
HashMap<String, AttributeValueUpdate> updates = new HashMap<String, AttributeValueUpdate>();
updates.put(key, avu);

UpdateItemRequest request = new UpdateItemRequest()
        .withTableName(PropertyLoader.getInstance().getTestTableName())
        .withKey(keyMap).withAttributeUpdates(updates);

ddb.updateItem(request);

List Deletion (Table Deletion)

The easiest way to remove all the user preference data is to delete the Amazon DynamoDB table. The following code show how.

  • iOS

DynamoDBDeleteTableRequest *request = [[[DynamoDBDeleteTableRequest alloc] initWithTableName:TEST_TABLE_NAME] autorelease];
DynamoDBDeleteTableResponse *response = [[AmazonClientManager ddb] deleteTable:request];

  • Android

AmazonDynamoDBClient ddb = UserPreferenceDemoActivity.clientManager.ddb();

DeleteTableRequest request = new DeleteTableRequest()
        .withTableName(PropertyLoader.getInstance().getTestTableName());
ddb.deleteTable(request);

Conclusion and Additional Resources

The code in this article demonstrates how to use Amazon DynamoDB as a storage device for your mobile application. You can find more information about Amazon DynamoDB here.

A sample app that includes this code is hosted in our samples repositories on GitHub:

For more information about using AWS credentials with mobile applications see the following article:

Want to learn more?

To learn about mobile development best practices, follow our AWS Mobile Development Blog. You can also ask questions or post comments in the Mobile Development Forum about this or any other topic related to mobile development with AWS.

©2014, Amazon Web Services, Inc. or its affiliates. All rights reserved.