I have recently implemented the AWS SDK for my iOS app, which I am developing in Swift. I have connected to my DB instance and am able to get a query response, however, I am struggling to convert this into usable data. I'm relatively new to Swift, AWS and programming in general so maybe missing something obvious!
My code is as follows:
let atVal = AWSDynamoDBAttributeValue()
atVal.S = "123456abc"
let condition = AWSDynamoDBCondition()
condition.comparisonOperator = AWSDynamoDBComparisonOperator.EQ
condition.attributeValueList = [atVal]
let myDic: [String: AWSDynamoDBCondition] = ["userid": condition]
let query = AWSDynamoDBQueryInput()
query.indexName = "userid-index"
query.tableName = "users"
query.keyConditions = myDic
query.limit = 1
dynamoDB.query(query).continueWithBlock {
(task: BFTask!) -> AnyObject! in
let results = task.result as AWSDynamoDBQueryOutput
let myResults = results.items
println("object: \(myResults.description)")
return nil
}
And the console output for this is:
object: [{ area = " {\n S = \"West Hampstead\";\n}"; name = " {\n S = \"Olly Mayes\";\n}"; userid = " {\n S = \"123456abc\";\n}"; }]
There doesn't seem to be much precedence for using AWS and Swift, understandably, so any help would be much appreciated!
You can increase your DynamoDB throughput by several times, by parallelizing reads/writes over multiple partitions. Use DynamoDB as an attribute store rather than as a document store. This will not only reduce the read/write costs but also improve the performance of your operations considerably.
Querying is a very powerful operation in DynamoDB. It allows you to select multiple Items that have the same partition ("HASH") key but different sort ("RANGE") keys.
Amazon DynamoDB supports PartiQL , a SQL-compatible query language, to select, insert, update, and delete data in Amazon DynamoDB.
Thus, we can simply have the data come in via API Gateway and get injected directly into DynamoDB (with some basic data transformation, and integration of the user's ID). This alleviates the need for a Lambda, and avoids the cost of that.
The easy answer to your question is : convert the JSON string returned into a Dictionary object. Something like this :
let data = jsonDataItem.dataUsingEncoding(NSUTF8StringEncoding)
if data != nil {
var error : NSError?
let dict = NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.AllowFragments, error: &error) as? NSDictionary
if let e = error {
...
} else {
...
}
However, I would strongly encourage you to look at the higher level DynamoDB mapper class When using DynamoDB mapper class, you define your data structure, and just give it to DynamoDB Mapper. Here is a full sample, from table creation to table deletion. The example inserts, removes, scans and query the table using DynamoDB Mapper.
First, you need to initialise the client SDK
let cp = AWSStaticCredentialsProvider(accessKey: "AK...", secretKey: "xxx")
let configuration = AWSServiceConfiguration(region: AWSRegionType.USEast1, credentialsProvider: cp)
AWSServiceManager.defaultServiceManager().setDefaultServiceConfiguration(configuration)
This is a sample only. It is not a good practice to embed an Access Key and Secret Key in the code. Best practice would be to use the AWSCognitoCredentialsProvider instead (read more about Cognito).
Define a class that maps your items in the table
class Item : AWSDynamoDBModel, AWSDynamoDBModeling {
var email : String = ""
var date : String = ""
var note : String = ""
var number : Double = 0.0
override init!() { super.init() }
required init!(coder: NSCoder!) {
fatalError("init(coder:) has not been implemented")
}
class func dynamoDBTableName() -> String! {
return "Demo"
}
class func hashKeyAttribute() -> String! {
return "email"
}
class func rangeKeyAttribute() -> String! {
return "date"
}
//required to let DynamoDB Mapper create instances of this class
override init(dictionary dictionaryValue: [NSObject : AnyObject]!, error: NSErrorPointer) {
super.init(dictionary: dictionaryValue, error: error)
}
//workaround to possible XCode 6.1 Bug : "Type NotificationAck" does not conform to protocol "NSObjectProtocol"
override func isEqual(anObject: AnyObject?) -> Bool {
return super.isEqual(anObject)
} }
To create a table
self.createTable().continueWithSuccessBlock {(task: BFTask!) -> BFTask! in
NSLog("Create table - success")
return nil
}
func createTable() -> BFTask! {
let pt = AWSDynamoDBProvisionedThroughput()
pt.readCapacityUnits = 10
pt.writeCapacityUnits = 10
let emailAttr = AWSDynamoDBAttributeDefinition()
emailAttr.attributeName = "email"
emailAttr.attributeType = AWSDynamoDBScalarAttributeType.S
let dateAttr = AWSDynamoDBAttributeDefinition()
dateAttr.attributeName = "date"
dateAttr.attributeType = AWSDynamoDBScalarAttributeType.S
let emailKey = AWSDynamoDBKeySchemaElement()
emailKey.attributeName = "email"
emailKey.keyType = AWSDynamoDBKeyType.Hash
let dateKey = AWSDynamoDBKeySchemaElement()
dateKey.attributeName = "date"
dateKey.keyType = AWSDynamoDBKeyType.Range
let ct = AWSDynamoDBCreateTableInput()
ct.tableName = "Demo"
ct.provisionedThroughput = pt
ct.attributeDefinitions = [emailAttr, dateAttr]
ct.keySchema = [ emailKey, dateKey ]
NSLog("Creating table")
let client = AWSDynamoDB.defaultDynamoDB()
return client.createTable(ct)
}
To delete a table
self.deleteTable().continueWithSuccessBlock({ (task: BFTask!) -> BFTask! in
NSLog("Delete table - success")
return nil
})
func deleteTable() -> BFTask! {
let dt = AWSDynamoDBDeleteTableInput()
dt.tableName = "Demo"
NSLog("Deleting table")
let client = AWSDynamoDB.defaultDynamoDB()
return client.deleteTable(dt)
}
To insert items
self.insertSomeItems().continueWithBlock({
(task: BFTask!) -> BFTask! in
if (task.error != nil) {
NSLog(task.error.description)
} else {
NSLog("DynamoDB save succeeded")
}
return nil;
})
func insertSomeItems() -> BFTask! {
let mapper = AWSDynamoDBObjectMapper.defaultDynamoDBObjectMapper()
var item = Item()
item.email = "[email protected]"
item.date = "20141101"
item.note = "This is item #1"
item.number = 1.0
let task1 = mapper.save(item)
item = Item()
item.email = "[email protected]"
item.date = "20141102"
item.note = "This is item #2"
item.number = 2.0
let task2 = mapper.save(item)
item = Item()
item.email = "[email protected]"
item.date = "20141103"
item.note = "This is item #3"
item.number = 3.0
let task3 = mapper.save(item)
return BFTask(forCompletionOfAllTasks: [task1, task2, task3])
}
To load one single item
self.load("[email protected]", range:"20141101").continueWithSuccessBlock({ (task: BFTask!) -> BFTask! in
NSLog("Load one value - success")
let item = task.result as Item
print(item)
return nil
})
func load(hash: String, range: String) -> BFTask! {
let mapper = AWSDynamoDBObjectMapper.defaultDynamoDBObjectMapper()
return mapper.load(Item.self, hashKey: hash, rangeKey: range)
}
To query on hash and range key
/*
keyConditions
http://docs.aws.amazon.com/AWSiOSSDK/latest/Classes/AWSDynamoDBQueryInput.html#//api/name/keyConditions
*/
let cond = AWSDynamoDBCondition()
let v1 = AWSDynamoDBAttributeValue(); v1.S = "20141101"
cond.comparisonOperator = AWSDynamoDBComparisonOperator.EQ
cond.attributeValueList = [ v1 ]
let c = [ "date" : cond ]
self.query("[email protected]", keyConditions:c).continueWithSuccessBlock({ (task: BFTask!) -> BFTask! in
NSLog("Query multiple values - success")
let results = task.result as AWSDynamoDBPaginatedOutput
for r in results.items {
print(r)
}
return nil
})
func query(hash: String, keyConditions:[NSObject:AnyObject]) -> BFTask! {
let mapper = AWSDynamoDBObjectMapper.defaultDynamoDBObjectMapper()
let exp = AWSDynamoDBQueryExpression()
exp.hashKeyValues = hash
exp.rangeKeyConditions = keyConditions
return mapper.query(Item.self, expression: exp)
}
To scan items (full table scan)
let cond = AWSDynamoDBCondition()
let v1 = AWSDynamoDBAttributeValue(); v1.S = "20141101"
cond.comparisonOperator = AWSDynamoDBComparisonOperator.GT
cond.attributeValueList = [ v1 ]
let exp = AWSDynamoDBScanExpression()
exp.scanFilter = [ "date" : cond ]
self.scan(exp).continueWithSuccessBlock({ (task: BFTask!) -> BFTask! in
NSLog("Scan multiple values - success")
let results = task.result as AWSDynamoDBPaginatedOutput
for r in results.items {
print(r)
}
return nil
})
func scan(expression : AWSDynamoDBScanExpression) -> BFTask! {
let mapper = AWSDynamoDBObjectMapper.defaultDynamoDBObjectMapper()
return mapper.scan(Item.self, expression: expression)
}
If you have no other usage of DynamoDB in US EAST 1, there is no cost associated with running this sample, as it is falling into DynamoDB's free tier.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With