CloudKit - CKQueryOperation with dependency - ios

I'm just beginning working with CloudKit, so bear with me.
Background info
At WWDC 2015, apple gave a talk about CloudKit https://developer.apple.com/videos/wwdc/2015/?id=715
In this talk, they warn against creating chaining queries and instead recommend this tactic:
let firstFetch = CKFetchRecordsOperation(...)
let secondFetch = CKFetchRecordsOperation(...)
...
secondFetch.addDependency(firstFetch)
letQueue = NSOperationQueue()
queue.addOperations([firstFetch, secondFetch], waitUntilFinished: false)
Example structure
The test project database contains pets and their owners, it looks like this:
|Pets | |Owners |
|-name | |-firstName |
|-birthdate | |-lastName |
|-owner (Reference) | | |
My Question
I am trying to find all pets that belong to an owner, and I'm worried I'm creating the chain apple warns against. See below for two methods that do the same thing, but two ways. Which is more correct or are both wrong? I feel like I'm doing the same thing but just using completion blocks instead.
I'm confused about how to change otherSearchBtnClick: to use dependency. Where would I need to add
ownerQueryOp.addDependency(queryOp)
in otherSearchBtnClick:?
#IBAction func searchBtnClick(sender: AnyObject) {
var petString = ""
let container = CKContainer.defaultContainer()
let publicDatabase = container.publicCloudDatabase
let privateDatabase = container.privateCloudDatabase
let predicate = NSPredicate(format: "lastName == '\(ownerLastNameTxt.text)'")
let ckQuery = CKQuery(recordType: "Owner", predicate: predicate)
publicDatabase.performQuery(ckQuery, inZoneWithID: nil) {
record, error in
if error != nil {
println(error.localizedDescription)
} else {
if record != nil {
for owner in record {
let myRecord = owner as! CKRecord
let myReference = CKReference(record: myRecord, action: CKReferenceAction.None)
let myPredicate = NSPredicate(format: "owner == %#", myReference)
let petQuery = CKQuery(recordType: "Pet", predicate: myPredicate)
publicDatabase.performQuery(petQuery, inZoneWithID: nil) {
record, error in
if error != nil {
println(error.localizedDescription)
} else {
if record != nil {
for pet in record {
println(pet.objectForKey("name") as! String)
}
}
}
}
}
}
}
}
}
#IBAction func otherSearchBtnClick (sender: AnyObject) {
let container = CKContainer.defaultContainer()
let publicDatabase = container.publicCloudDatabase
let privateDatabase = container.privateCloudDatabase
let queue = NSOperationQueue()
let petPredicate = NSPredicate(format: "lastName == '\(ownerLastNameTxt.text)'")
let petQuery = CKQuery(recordType: "Owner", predicate: petPredicate)
let queryOp = CKQueryOperation(query: petQuery)
queryOp.recordFetchedBlock = { (record: CKRecord!) in
println("recordFetchedBlock: \(record)")
self.matchingOwners.append(record)
}
queryOp.queryCompletionBlock = { (cursor: CKQueryCursor!, error: NSError!) in
if error != nil {
println(error.localizedDescription)
} else {
println("queryCompletionBlock: \(cursor)")
println("ALL RECORDS ARE: \(self.matchingOwners)")
for owner in self.matchingOwners {
let ownerReference = CKReference(record: owner, action: CKReferenceAction.None)
let ownerPredicate = NSPredicate(format: "owner == %#", ownerReference)
let ownerQuery = CKQuery(recordType: "Pet", predicate: ownerPredicate)
let ownerQueryOp = CKQueryOperation(query: ownerQuery)
ownerQueryOp.recordFetchedBlock = { (record: CKRecord!) in
println("recordFetchedBlock (pet values): \(record)")
self.matchingPets.append(record)
}
ownerQueryOp.queryCompletionBlock = { (cursor: CKQueryCursor!, error: NSError!) in
if error != nil {
println(error.localizedDescription)
} else {
println("queryCompletionBlock (pet values)")
for pet in self.matchingPets {
println(pet.objectForKey("name") as! String)
}
}
}
publicDatabase.addOperation(ownerQueryOp)
}
}
}
publicDatabase.addOperation(queryOp)
}

If you don't need cancellation and aren't bothered about retrying on a network error then I think you are fine chaining the queries.
I know I know, in WWDC 2015 Nihar Sharma recommended the add dependency approach but it would appear he just threw that in at the end without much thought. You see it isn't possible to retry a NSOperation because they are one-shot anyway, and he offered no example for cancelling operations already in the queue, or how to pass data from one operation from the next. Given these 3 complications that could take you weeks to solve, just stick with what you have working and wait for the next WWDC for their solution. Plus the whole point of blocks is to let you call inline methods and be able to access the params in the method above, so if you move to operations you kind of don't get full advantage of that benefit.
His main reason for not using chaining is the ridiculous one that he couldn't tell which error is for which request, he had names his errors someError then otherError etc. No one in their right mind names error params different inside blocks so just use the same name for all of them and then you know inside a block you are always using the right error. Thus he was the one that created his messy scenario and offered a solution for it, however the best solution is just don't create the messy scenario of multiple error param names in the first place!
With all that being said, in case you still want to try to use operation dependencies here is an example of how it could be done:
__block CKRecord* venueRecord;
CKRecordID* venueRecordID = [[CKRecordID alloc] initWithRecordName:#"4c31ee5416adc9282343c19c"];
CKFetchRecordsOperation* fetchVenue = [[CKFetchRecordsOperation alloc] initWithRecordIDs:#[venueRecordID]];
fetchVenue.database = [CKContainer defaultContainer].publicCloudDatabase;
// init a fetch for the category, it's just a placeholder just now to go in the operation queue and will be configured once we have the venue.
CKFetchRecordsOperation* fetchCategory = [[CKFetchRecordsOperation alloc] init];
[fetchVenue setFetchRecordsCompletionBlock:^(NSDictionary<CKRecordID *,CKRecord *> * _Nullable recordsByRecordID, NSError * _Nullable error) {
venueRecord = recordsByRecordID.allValues.firstObject;
CKReference* ref = [venueRecord valueForKey:#"category"];
// configure the category fetch
fetchCategory.recordIDs = #[ref.recordID];
fetchCategory.database = [CKContainer defaultContainer].publicCloudDatabase;
}];
[fetchCategory setFetchRecordsCompletionBlock:^(NSDictionary<CKRecordID *,CKRecord *> * _Nullable recordsByRecordID, NSError * _Nullable error) {
CKRecord* categoryRecord = recordsByRecordID.allValues.firstObject;
// here we have a venue and a category so we could call a completion handler with both.
}];
NSOperationQueue* queue = [[NSOperationQueue alloc] init];
[fetchCategory addDependency:fetchVenue];
[queue addOperations:#[fetchVenue, fetchCategory] waitUntilFinished:NO];
How it works is first it vetches a Venue record, then it fetches its Category.
Sorry there is no error handling but as you can see it was already a ton of code to do something can could be done in a couple of lines with chaining. And personally I find this result more convoluted and confusing than simply chaining together the convenience methods.

in theory you could have multiple owners and therefore multiple dependencies. Also the inner queries will be created after the outer query is already executed. You will be too late to create a dependency. In your case it's probably easier to force the execution of the inner queries to a separate queue like this:
if record != nil {
for owner in record {
NSOperationQueue.mainQueue().addOperationWithBlock {
This way you will make sure that every inner query will be executed on a new queue and in the mean time that parent query can finish.
Something else: to make your code cleaner, it would be better if all the code inside the for loop was in a separate function with a CKReference as a parameter.

I had the same problem recently and ended up using a NSBlockOperation to prepare the second query and added a dependency to make it all work:
let container = CKContainer.defaultContainer()
let publicDB = container.publicCloudDatabase
let operationqueue = NSOperationQueue.mainQueue()
let familyPredicate = NSPredicate(format: "name == %#", argumentArray: [familyName])
let familyQuery = CKQuery(recordType: "Familias", predicate: familyPredicate)
let fetchFamilyRecordOp = CKQueryOperation(query: familyQuery)
fetchFamilyRecordOp.recordFetchedBlock = { record in
familyRecord = record
}
let fetchMembersOP = CKQueryOperation()
// Once we have the familyRecord, we prepare the PersonsFetch
let prepareFamilyRef = NSBlockOperation() {
let familyRef = CKReference(record: familyRecord!, action: CKReferenceAction.None)
let familyRecordID = familyRef?.recordID
let membersPredicate = NSPredicate(format: "familia == %#", argumentArray: [familyRecordID!])
let membersQuery = CKQuery(recordType: "Personas", predicate: membersPredicate)
fetchMembersOP.query = membersQuery
}
prepareFamilyRef.addDependency(fetchFamilyRecordOp)
fetchMembersOP.recordFetchedBlock = { record in
members.append(record)
}
fetchMembersOP.addDependency(prepareFamilyRef)
fetchMembersOP.database = publicDB
fetchFamilyRecordOp.database = publicDB
operationqueue.addOperations([fetchFamilyRecordOp, fetchMembersOP, prepareFamilyRef], waitUntilFinished: false)
And now it's working as i expected, because you can set up your operations in a very granular way and they execute in the correct order ^.^
in your case i would structure it like this:
let predicate = NSPredicate(format: "lastName == '\(ownerLastNameTxt.text)'")
let ckQuery = CKQuery(recordType: "Owner", predicate: predicate)
let getOwnerOperation = CKQueryOperation(query: ckQuery)
getOwnerOperation.recordFetchedBlock = { record in
let name = record.valueForKey("name") as! String
if name == myOwnerName {
ownerRecord = record
}
}
//now we have and operation that will save in our var OwnerRecord the record that is exactly our owner
//now we create another that will fetch our pets
let queryPetsForOurOwner = CKQueryOperation()
queryPetsForOurOwner.recordFetchedBlock = { record in
results.append(record)
}
//That's all this op has to do, BUT it needs the owner operation to be completed first, but not inmediately, we need to prepare it's query first so:
var fetchPetsQuery : CKQuery?
let preparePetsForOwnerQuery = NSBlockOperation() {
let myOwnerRecord = ownerRecord!
let ownerRef = CKReference(record: myOwnerRecord, action: CKReferenceAction.None)
let myPredicate = NSPredicate(format: "owner == %#", myReference)
fetchPetsQuery = CKQuery(recordType: "Pet", predicate: myPredicate)
}
queryPetsForOurOwner.query = fetchPetsQuery
preparePetsForOwnerQuery.addDependency(getOwnerOperation)
queryPetsForOurOwner.addDependency(preparePetsForOwnerQuery)
and now all it needs to be done is to add them to the newly created operation queue after we direct them to our database
getOwnerOperation.database = publicDB
queryPetsForOurOwner.database = publicDB
let operationqueue = NSOperationQueue.mainQueue()
operationqueue.addOperations([getOwnerOperation, queryPetsForOurOwner, preparePetsForOwnerQuery], waitUntilFinished: false)
P.S: i know i said Family and Person and the names are not like that, but i'm spanish and testing some cloudkit operations, so i haven't standardized to english recor type names yet ;)

Related

FetchRequst issue with data fault

When I was inserting data to one entity of CoreData, All the rows are inserted successfully(Saved).
But when I try to fetch the data using FetchRequest, Only one row of data is coming even if number of rows inserted are 3 or 4 or anything(more than 1).
Remaining rows are not getting fetched. And when I print fetch results,
It says - Error
0:<EquipmentDetails: 0x6000000bad60>
(entity: EquipmentDetails; id: 0xd000000000040000
coredata:/EquipmentDetails/p1> **data:fault>)**
I didn't get what was going in backend of core data?
code for Insertion
func insertEqipToLocalDb()
{
let mobileNo : String = UserDefaults.standard.string(forKey: "phoneNumber")!
let equipDetailsItem = NSEntityDescription.insertNewObject(forEntityName: "EquipmentDetails", into:managedObjContext) as! EquipmentDetails
for (index,item) in array_IDEquip.enumerated()
{
equipDetailsItem.mobileNumber = mobileNo
equipDetailsItem.type = array_typeEquip[index]
equipDetailsItem.name = array_nameEquip[index]
equipDetailsItem.startDate = array_sDateEquip[index]
equipDetailsItem.endDate = array_eDateEquip[index]
equipDetailsItem.equpID = Int16(item)
equipDetailsItem.serviceDatesStr = array_serviceDateEquip[index]
}
do
{
try managedObjContext.save()
UserDefaults.standard.set("AlreadyInstalled", forKey: "statusInstallation")
}
catch
{
Exception.insertExceptionDetails(errorMsg: error as NSError, context: managedObjContext)
}
}
//code for fetching
let request = NSFetchRequest<NSFetchRequestResult>()
let entity = NSEntityDescription.entity(forEntityName:"EquipmentDetails", in: managedObjContext)
request.entity = entity
do
{
let fetchResults = try managedObjContext.fetch(request)
for r in fetchResults
{
typeEquipArray.append((r as AnyObject).value(forKey: "type") as! String)
}
}
catch let error as NSError
{
Exception.insertExceptionDetails(errorMsg: error, context: managedObjContext)
}
On this line:
let equipDetailsItem = NSEntityDescription.insertNewObject(forEntityName: "EquipmentDetails", into:managedObjContext) as! EquipmentDetails
You create one instance. In the loop that follows, you set values for the type, name, etc properties over and over again on that same instance. Then you save changes, which include just that one object. If you want a difference instance of EquipmentDetails for each pass through the loop, you need to create the instance inside the loop.
The "fault" message is not an error unless you tried to access the property values and found that they were not present. It's part of how Core Data works. See the answer that Harshal Valanda linked in the comments for more detail.

Faster way to check if entry exists in Core Data

In my app when data is synced i can get 20k entries (from given timestamp) from the server that should be synced to the local device. For every entry i try to fetch it (if it exist already) and if doesn't i create new. The problem is that the whole operation is too slow - for 20k on iphone 5 is 10+ mins. Another solution that i though is to delete all entries from the given timestamp and create new entries for all returned entries and there will be no need to perform fetch for every single entry ? If someone have any advice will be nice. Here is sample code for the current state:
var logEntryToUpdate:LogEntry!
if let savedEntry = CoreDataRequestHelper.getLogEntryByID(inputID: inputID, fetchAsync: true) {
logEntryToUpdate = savedEntry
} else {
logEntryToUpdate = LogEntry(entity: logEntryEntity!, insertInto: CoreDataStack.sharedInstance.saveManagedObjectContext)
}
logEntryToUpdate.populateWithSyncedData(data: row, startCol: 1)
Here is the actual request method:
class func getLogEntryByID(inputID:Int64, fetchAsync:Bool) ->LogEntry? {
let logEntryRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "LogEntry")
logEntryRequest.predicate = NSPredicate(format: "inputId == %#", NSNumber(value: inputID as Int64))
logEntryRequest.fetchLimit = 1
do {
let mocToFetch = fetchAsync ? CoreDataStack.sharedInstance.saveManagedObjectContext : CoreDataStack.sharedInstance.managedObjectContext
if let fetchResults = try mocToFetch.fetch(logEntryRequest) as? [LogEntry] {
if ( fetchResults.count > 0 ) {
return fetchResults[0]
}
return nil
}
} catch let error as NSError {
NSLog("Error fetching Log Entries by inputID from core data !!! \(error.localizedDescription)")
}
return nil
}
Another thing that i tried is to check the count for specific request but again is too slow.
class func doesLogEntryExist(inputID:Int64, fetchAsync:Bool) ->Bool {
let logEntryRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "LogEntry")
logEntryRequest.predicate = NSPredicate(format: "inputId == %#", NSNumber(value: inputID as Int64))
//logEntryRequest.resultType = .countResultType
logEntryRequest.fetchLimit = 1
do {
let mocToFetch = fetchAsync ? CoreDataStack.sharedInstance.saveManagedObjectContext : CoreDataStack.sharedInstance.managedObjectContext
let count = try mocToFetch.count(for: logEntryRequest)
if ( count > 0 ) {
return true
}
return false
} catch let error as NSError {
NSLog("Error fetching Log Entries by inputID from core data !!! \(error.localizedDescription)")
}
return false
}
Whether fetching the instance or getting the count, you're still doing one fetch request per incoming record. That's going to be slow, and your code will be spending almost all of its time performing fetches.
One improvement is to batch up the records to reduce the number of fetches. Get multiple record IDs into an array, and then fetch all of them at once with a predicate like
NSPredicate(format: "inputId IN %#", inputIdArray)
Then go through the results of the fetch to see which IDs were found. Accumulate 50 or 100 IDs in the array, and you'll reduce the number of fetches by 50x or 100x.
Deleting all the entries for the timestamp and then re-inserting them might be good, but it's hard to predict. You'll have to insert all 20,000. Is that faster or slower than reducing the number of fetches? It's impossible to say for sure.
Based on Paulw11's comment, I came up with the following method to evaluate Structs being imported into Core Data.
In my example, I have a class where I store search terms. Within the search class, create a predicate which describes the values of the stuff within my array of structs.
func importToCoreData(dataToEvaluateArray: [YourDataStruct]) {
// This is what Paul described in his comment
let newDataToEvaluate = Set(dataToEvaluateArray.map{$0.id})
let recordsInCoreData = getIdSetForCurrentPredicate()
let newRecords = newDataToEvaluate.subtracting(recordsInCoreData)
// create an empty array
var itemsToImportArray: [YourDataStruct] = []
// and dump records with ids contained in newRecords into it
dataToEvaluateArray.forEach{ record in
if newRecords.contains(record.id) {
itemsToImportArray.append(record)
}
}
// THEN, import if you need to
itemsToImportArray.forEach { struct in
// set up your entity, properties, etc.
}
// Once it's imported, save
// You can save each time you import a record, but it'll go faster if you do it once.
do {
try self.managedObjectContext.save()
} catch let error {
self.delegate?.errorAlert(error.localizedDescription, sender: self)
}
self.delegate?.updateFetchedResultsController()
}
To instantiate recordsInCoreData, I created this method, which returns a Set of unique identifiers that exist in the managedObjectContext:
func getIdSetForCurrentPredicate() -> Set<String> {
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "YourEntity")
// searchQuery is a class I created with a computed property for a creating a predicate. You'll need to write your own predicate
fetchRequest.predicate = searchQuery.predicate
var existingIds: [YourEntity] = []
do {
existingIds = try managedObjectContext.fetch(fetchRequest) as! [YourEntity]
} catch let error {
delegate?.errorAlert(error.localizedDescription, sender: self)
}
return Set<String>(existingIds.map{$0.id})
}

Getting zero records when using NSPredicate

Everytime I use NSPredicate in fetching my records from CoreData, I always get zero or no records at all. Here's my function:
func get_facility(company: String, territory: String){
print("company: " + company)
print("territory: " + territory)
var appDel:AppDelegate = (UIApplication.sharedApplication().delegate as! AppDelegate)
var context:NSManagedObjectContext = appDel.managedObjectContext
let companyPredicate = NSPredicate(format: "company LIKE %#", company)
let territoryPredicate = NSPredicate(format: "territory LIKE %#", territory)
let predicate = NSCompoundPredicate(type: NSCompoundPredicateType.OrPredicateType, subpredicates: [companyPredicate, territoryPredicate])
let fetchRequestFacility = NSFetchRequest(entityName:"Facility")
fetchRequestFacility.predicate = predicate
do {
let facility_temp = try context.executeFetchRequest(fetchRequestFacility)
facility = facility_temp as! [Facility]
c = facility_temp.count
print(c)
tableview_facility.reloadData()
} catch let error as NSError {
// failure
print("Fetch failed: \(error.localizedDescription)")
}
}
When I comment out the the line fetchRequestFacility.predicate = predicate it shows all of my records. The fields company and territory are of type String.
I also tried changing the LIKE keyword to == and =. But I still get no records.
Possible values for company = 1 and territory = 1. These are numbers but with String data type. And here's the structure of my facility entity:
I think I found the culprit, when I print the values of company and territory, I get the:
company: Optional(1)
territory: Optional(1)
And when I try this predicate: let companyPredicate = NSPredicate(format: "company = '1'"), I am able to fetch records. Maybe its because the variables company and territory have Optionals string. I don't know how to remove it. I tried putting ! to my variable but still the same.

how to perform a query by the field as well as the record type

I was wondering if it was possible to search for a CKRecord by not only its record type but as well as its field in the database. For example heres my code I have now to fetch records:
database.performQuery(query, inZoneWithID: nil, completionHandler: { (records:[AnyObject]!, error:NSError!) in
// Check if there's an error
if error != nil {
NSLog(error.localizedDescription)
}
else {
}
})
This only searches through the record type and for my program I need to be able to go deeper and specify a field as well to search through. Any help will be greatly appreciated!
The CKQuery documentation shows you how to build more complex queries. Here is a simple example:
let firstName = "John"
let age = 20
let predicate = NSPredicate(format: "firstName = %# AND age >= %#", firstName, NSNumber(integer: age))
let query = CKQuery(recordType: "Employee", predicate: predicate)

AWS DynamoDB Batch Get Request - iOS

I can perform a simple Get request on a singular table within AWS dynamoDB however when I expand it to a Batch Request across multiple tables I continue to get a error
validation error detected: Value null at 'requestItems.rip.member.keys' failed to satisfy constraint
I understand this as the values not being passed correctly but I can't see what the issue is with my code
//Create Request Values
AWSDynamoDBGetItemInput *getItem = [AWSDynamoDBGetItemInput new];
AWSDynamoDBAttributeValue *hashValue = [AWSDynamoDBAttributeValue new];
hashValue.S = #"User Test";
getItem.key = #{#"ripId": hashValue};
//Create Request Values 2
AWSDynamoDBGetItemInput *getItem2 = [AWSDynamoDBGetItemInput new];
AWSDynamoDBAttributeValue *hashValue2 = [AWSDynamoDBAttributeValue new];
hashValue2.S = #"User Test";
getItem2.key = #{#"chat": hashValue2};
//Combine to Batch Request
AWSDynamoDBBatchGetItemInput * batchFetch = [AWSDynamoDBBatchGetItemInput new];
batchFetch.requestItems = #{ #"rip": getItem,
#"chat": getItem,};
[[dynamoDB batchGetItem:batchFetch] continueWithBlock:^id(BFTask *task) {
if (!task.error) {
NSLog(#"BOY SUCCES");
} else {
NSLog(#" NO BOY SUCCESS %#",task.error);
}
return nil;
}];
Searched the internet high and low but cannot see a working example of a batch request using iOS Objective C (or swift for that matter).
I have tested both variables on a single Get request and they both work.
You forgot to wrap around AWSDynamoDBAttributeValue in AWSDynamoDBKeysAndAttributes. Here is a simple example from AWSDynamoDBTests.m:
AWSDynamoDBKeysAndAttributes *keysAndAttributes = [AWSDynamoDBKeysAndAttributes new];
keysAndAttributes.keys = #[#{#"hashKey" : attributeValue1},
#{#"hashKey" : attributeValue2}];
keysAndAttributes.consistentRead = #YES;
AWSDynamoDBBatchGetItemInput *batchGetItemInput = [AWSDynamoDBBatchGetItemInput new];
batchGetItemInput.requestItems = #{table1Name: keysAndAttributes};
Since the batch get doesn't map to a class I solved it by doing this instead.
I solved it by doing this,
let dynamoDBObjectMapper = AWSDynamoDBObjectMapper.defaultDynamoDBObjectMapper()
let task1 = dynamoDBObjectMapper.load(User.self, hashKey: "rtP1oQ5DJG", rangeKey: nil)
let task2 = dynamoDBObjectMapper.load(User.self, hashKey: "dbqb1zyUq1", rangeKey: nil)
AWSTask.init(forCompletionOfAllTasksWithResults: [task1, task2]).continueWithBlock { (task) -> AnyObject? in
if let users = task.result as? [User] {
print(users.count)
print(users[0].firstName)
print(users[1].firstName)
}
else if let error = task.error {
print(error.localizedDescription)
}
return nil
}
Swift 3
I was able to get the BatchGet request work with the following code. Hope this helps someone else who's struggling with the lack of Swift Docs.
This code assumes that you've configured your AWSServiceConfiguration in the AppDelegate application didFinishLaunchingWithOptions method.
let DynamoDB = AWSDynamoDB.default()
// define your primary hash keys
let hashAttribute1 = AWSDynamoDBAttributeValue()
hashAttribute1?.s = "NDlFRTdDODEtQzNCOC00QUI5LUFFMzUtRkIyNTJFNERFOTBF"
let hashAttribute2 = AWSDynamoDBAttributeValue()
hashAttribute2?.s = "MjVCNzU3MUQtMEM0NC00NEJELTk5M0YtRTM0QjVDQ0Q1NjlF"
let keys: Array = [["userID": hashAttribute1], ["userID": hashAttribute2]]
let keysAndAttributesMap = AWSDynamoDBKeysAndAttributes()
keysAndAttributesMap?.keys = keys as? [[String : AWSDynamoDBAttributeValue]]
keysAndAttributesMap?.consistentRead = true
let tableMap = ["Your-Table-Name" : keysAndAttributesMap]
let request = AWSDynamoDBBatchGetItemInput()
request?.requestItems = tableMap as? [String : AWSDynamoDBKeysAndAttributes]
request?.returnConsumedCapacity = AWSDynamoDBReturnConsumedCapacity.total
DynamoDB.batchGetItem(request!) { (output, error) in
if output != nil {
print("Batch Query output?.responses?.count:", output!.responses!)
}
if error != nil {
print("Batch Query error:", error!)
}
}

Resources