scope issue in recordFetchedBlock swift - ios

So I have this function, where I am comparing the password user entered(passed in variable 'password') with that available in the database( fetched in variable 'Pwd'). If the condition matches then I want the function to return value 1 else 0.
But I am facing issue with scope of variable. The count has the value 1 inside recordFetchedBlock only. and not outside.Can you suggest how I can make this function return 1 on password matching
func FetchRecords(user_name: String, password: String) -> Int {
let pubData = CKContainer.default().publicCloudDatabase
let predicate = NSPredicate(format: "Username = %#", user_name)
let query = CKQuery(recordType: "Teachers", predicate: predicate)
let operation = CKQueryOperation(query: query)
var Pwd = String()
var count = Int()
operation.recordFetchedBlock = { (record : CKRecord!) in
let Pwd: String = record["Password"] as! String
if Pwd == password{
count=1
}else{
count=0
}
}
operation.queryCompletionBlock = { cursor, error in
}
CKContainer.default().publicCloudDatabase.add(operation)
return count
}

Thanks alot for the help #vadian.
The code got fixed with Completion Handler.
Modified code looks like:
func FetchRecords(user_name: String, pass_word: String, completion: #escaping (Bool)->()) -> Void {
let pubData = CKContainer.default().publicCloudDatabase
let predicate = NSPredicate(format: "Username = %#", user_name)
let query = CKQuery(recordType: "Teachers", predicate: predicate)
let operation = CKQueryOperation(query: query)
operation.desiredKeys = ["Password"]
operation.recordFetchedBlock = { (record) in
if pass_word == (record["Password"] as! String){
print("Checkpoint1")
let flag: Bool = true
completion(flag)
}else{
print("Checkpoint2")
let flag: Bool = false
completion(flag)
}
}
operation.queryCompletionBlock = { [unowned self] (cursor, error) in
DispatchQueue.main.async {
if error == nil {
print("Checkpoint3")
}else{
let ac = UIAlertController(title: "Fetch failed", message: "There was a problem fetching records; please try again: \(error!.localizedDescription)", preferredStyle: .alert)
ac.addAction(UIAlertAction(title: "OK", style: .default))
self.present(ac, animated: true)
}
}
}
CKContainer.default().publicCloudDatabase.add(operation)
print("Checkpoint4")
}

Related

Swift - Get all records from Cloudkit Public Database

Hi i'm trying to fetch all records from my publicDB in cloudkit, and currently there are more of 2000 records. How can I fetch them all and put them in an array?
I've tryed these two methods without success. Can you please help me?
1 Approach
let predicate = NSPredicate(value: true)
let query = CKQuery(recordType: "Position", predicate: predicate)
query.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
publicDB.perform(query, inZoneWith: nil) { (results, error) -> Void in
if error != nil {
DispatchQueue.main.async(execute: { () -> Void in
self.delegate?.errorUpdating(error: error! as NSError)
return
})
} else {
self.positionArray.removeAll(keepingCapacity: true)
for record in results! {
let position = Position(record: record as CKRecord, database: self.publicDB)
self.positionArray.append(position)
}
}
DispatchQueue.main.async(execute: { () -> Void in
/* my elaboration with the complete result */
})
}
2 Approach
let predicate = NSPredicate(value: true)
let query = CKQuery(recordType: "Position", predicate: predicate)
query.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
let qop = CKQueryOperation(query: query)
qop.resultsLimit = 3000
qop.recordFetchedBlock = { (record: CKRecord) in
let position = Position(record: record, database: self.publicDB)
self.positionArray.append(position)
print(self.positionArray.count)
}
qop.queryCompletionBlock = { (cursor: CKQueryOperation.Cursor?, error: Error?) in
DispatchQueue.main.async(execute: { () -> Void in
if let cursor = cursor {
print("entro")
let newOperation = CKQueryOperation(cursor: cursor)
newOperation.recordFetchedBlock = qop.recordFetchedBlock
newOperation.queryCompletionBlock = qop.queryCompletionBlock
self.publicDB.add(newOperation)
}
else if let error = error {
print("Error:", error)
}
// No error and no cursor means the operation was successful
else {
print("Finished with records:", self.positionArray)
if(!all){
// my elaboration
}
else{
// my elaboration
}
}
})
}
self.publicDB.add(qop)
With the first approach I can fetch at most 100 records.
With the second approach I can fetch at most 400 records.
But i need to fill my array with the all over 2000 records, how can achieve the result?
Plese, Try this one!
var Whistle = [CKRecord] ()
func loadWhistles(completionHandler: (() -> Void)?) {
let pred = NSPredicate(value: true)
let sort = NSSortDescriptor(key: nomeDaTabela, ascending: ordemDaTabela)
let query = CKQuery(recordType: "Cliente", predicate: pred)
query.sortDescriptors = [sort]
let operation = CKQueryOperation(query: query)
operation.desiredKeys = ["Name"]
operation.resultsLimit = 2000
var newWhistles = Whistle
operation.recordFetchedBlock = { record in
newWhistles.append(record)
}
operation.queryCompletionBlock = { [unowned self] (cursor, error) in
DispatchQueue.main.async {
if error == nil {
//ViewController.isDirty = false
self.clienteRecords = newWhistles
self.clientesTableView.reloadData()
} else {
let ac = UIAlertController(title: "Fetch failed", message: "There was a problem fetching the list of whistles; please try again: \(error!.localizedDescription)", preferredStyle: .alert)
ac.addAction(UIAlertAction(title: "OK", style: .default))
self.present(ac, animated: true)
}
}
}
PublicDatabase.add(operation)
completionHandler?()
}
There is indeed a limit of 400 records per fetch operation, so you need to check CKQueryCursor value returned by query completion block, and if it is not nil start another operation with it CKQueryOperation(cursor: cursor).
So in principle your 2nd approach is correct. My guess is that your problem occurs due to threading issue, try removing DispatchQueue.main.async(execute: { () -> Void in.
P.S. Check out how it is done in RxCloudKit (which handles big fetches automatically), it definitely works for fetching 1000+ records - RecordFetcher.queryCompletionBlock(cursor: CKQueryCursor?, error: Error?)

How to write a dynamodb query in swift to fetch all values with a particular partition key

I am unable to find a way to write a dynamodb query in swift to fetch results that have a particular partition key.
For example say my partition key is "bigCompany" and sort key is "email". Now I want to fetch all emails with their "bigCompany" name as "xyz".
For reference my code structure is very similar to the load function below. But this one uses .load to fetch one value instead of querying. Basically I need to find a way to call dynamoDBOBjectMapper .query() given the constraints I mentioned above. Any help would be much much appreciated!
func getTableRow() {
let dynamoDBObjectMapper = AWSDynamoDBObjectMapper.defaultDynamoDBObjectMapper()
//tableRow?.UserId --> (tableRow?.UserId)!
dynamoDBObjectMapper .load(DDBTableRow.self, hashKey: (tableRow?.UserId)!, rangeKey: tableRow?.GameTitle) .continueWithExecutor(AWSExecutor.mainThreadExecutor(), withBlock: { (task:AWSTask!) -> AnyObject! in
if (task.error == nil) {
if (task.result != nil) {
let tableRow = task.result as! DDBTableRow
self.hashKeyTextField.text = tableRow.UserId
self.rangeKeyTextField.text = tableRow.GameTitle
self.attribute1TextField.text = tableRow.TopScore?.stringValue
self.attribute2TextField.text = tableRow.Wins?.stringValue
self.attribute3TextField.text = tableRow.Losses?.stringValue
}
} else {
print("Error: \(task.error)")
let alertController = UIAlertController(title: "Failed to get item from table.", message: task.error!.description, preferredStyle: UIAlertControllerStyle.Alert)
let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.Cancel, handler: { (action:UIAlertAction) -> Void in
})
alertController.addAction(okAction)
self.presentViewController(alertController, animated: true, completion: nil)
}
return nil
})
}
Figured it out. They key is using keyConditionExpression correctly. Below is my answer.
let queryExpression = AWSDynamoDBQueryExpression()
queryExpression.keyConditionExpression = "#bigCompany = :bigCompany"
queryExpression.expressionAttributeNames = ["#bigCompany": "bigCompany",]
queryExpression.expressionAttributeValues = [":bigCompany" : self.bigCompany,]
dynamoDBObjectMapper.query(Requests.self, expression: queryExpression) .continue(with: AWSExecutor.immediate(), with: { (task:AWSTask!) -> AnyObject! in
//If added successfully
if((task.result) != nil) {
let results = task.result! as AWSDynamoDBPaginatedOutput
print(results.items)
}
else {
//do error checking here
}
})

CloudKit private database returns first 100 CKRecords

i am facing this kind of problem working with CloudKit. Trying to fetch all data from "Data" record. Result is limited by 100. How to get all data? Please, thank for any advice.
func getAllDataFromCloudKit(){
let predicate = NSPredicate(value: true)
let container = CKContainer.defaultContainer()
let privateDatabase = container.privateCloudDatabase
let query = CKQuery(recordType: "Data", predicate: predicate)
privateDatabase.performQuery(query, inZoneWithID: nil) { results, error in
if error != nil {
print(error)
}
else {
for result in results! {
// return only 100 first
}
}
}
}
P.S. i found one similar question, still not clear or answer is too old and does not work with the new Swift version
EDIT: See my final solution how to get all data from private database below:
Ok, i found a solution. See below:
func loadDataFromCloudKit() {
var results: [AnyObject] = []
let cloudContainer = CKContainer.defaultContainer()
let privateDatabase = cloudContainer.privateCloudDatabase
let predicate = NSPredicate(value: true)
let query = CKQuery(recordType: "Data", predicate: predicate)
query.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: true)]
let queryOperation = CKQueryOperation(query: query)
queryOperation.desiredKeys = ["id","name"]
queryOperation.queuePriority = .VeryHigh
// Max limit is still 100
queryOperation.resultsLimit = 100
queryOperation.recordFetchedBlock = { (record:CKRecord!) -> Void in
results.append(record)
}
queryOperation.queryCompletionBlock = { (cursor, error) in
dispatch_async(dispatch_get_main_queue()) {
if (error != nil) {
print("Failed to get data from iCloud - \(error!.localizedDescription)")
} else {
print("Successfully retrieve the data form iCloud")
}
}
// see cursor here, if it is nil than you have no more records
// if it has a value than you have more records to get
if cursor != nil {
print("there is more data to fetch")
let newOperation = CKQueryOperation(cursor: cursor!)
newOperation.recordFetchedBlock = { (record:CKRecord!) -> Void in
results.append(record)
}
newOperation.queryCompletionBlock = queryOperation.queryCompletionBlock
privateDatabase.addOperation(newOperation)
} else {
// gets more then 100
print(results.count)
}
}
privateDatabase.addOperation(queryOperation)
}
Kevin,
You use the cursor returned by CKQueryOperation; it is very much a standard approach in the world of databases; I know some dropbox operations use the same approach for example. Here is the basic code for a CKQueryOperation.
func query4Cloud(theLink: String, theCount: Int) {
var starCount:Int = 0
let container = CKContainer(identifier: "iCloud.ch")
let publicDB = container.publicCloudDatabase
let singleLink2LinkthemALL = CKRecordID(recordName: theLink)
let recordToMatch = CKReference(recordID: singleLink2LinkthemALL, action: .DeleteSelf)
let predicate = NSPredicate(format:"theLink == %#", recordToMatch)
let query = CKQuery(recordType: "Files", predicate: predicate)
// You run the query operation replacing query with your cursor
readerOperation = CKQueryOperation(query: query)
readerOperation.desiredKeys = ["record.recordID.recordName"];
readerOperation.recordFetchedBlock = { (record) in
starCount += 1
}
// see cursor here, if it is nil than you have no more records
// if it has a value than you have more records to get
readerOperation.queryCompletionBlock = {(cursor, error) in
print("fcuk query4Cloud \(theLink) \(theCount) \(starCount)" )
if error != nil {
self.showAlert(message: error!.localizedDescription)
print("ting, busted")
} else {
// it's done
}
}
print("publicDB.addOperation(operation)")
readerOperation.qualityOfService = .Background
publicDB.addOperation(readerOperation)
}

Update row in Parse.com

How can i upload a row in Parse.com?
This is my query code:
func queryFromParse(){
self.arrayOfDetails.removeAll()
let query = PFQuery(className: "currentUploads")
query.orderByDescending("createdAt")
query.findObjectsInBackgroundWithBlock { (objects:[AnyObject]?, error:NSError?) -> Void in
if error == nil
{
if let newObjects = objects as? [PFObject] {
for oneobject in newObjects {
let text = oneobject["imageText"] as! String
let username = oneobject["username"] as! String
let deviceID = oneobject["identifierForVendor"] as! String
let reportedCount = oneobject["reportedCount"] as! String
let time = oneobject.createdAt!
if let userImage = oneobject["imageFile"] as? PFFile {
let userImage = oneobject["imageFile"] as! PFFile
let imageURL = userImage.url // <- Bruker nĂ¥ userImage.URL, henter ikke bildefilen med en gang
let OneBigObject = Details(username: username, text: text, CreatedAt: time, image: imageURL!, deviceID: deviceID, reportedCount: reportedCount)
//let OneBigObject = Details(username: username, text: text, CreatedAt: time, image: imageURL!)
self.arrayOfDetails.append(OneBigObject)
dispatch_async(dispatch_get_main_queue()) { self.collectionView.reloadData() }
}
}
}
}
}
}
Image here
I want to update the "reportedCount" when the image is reported. I have a code that i have been using, but that created new rows in another class for each report, and I want only to update the "reportedCount":
#IBAction func reportContentAction(sender: AnyObject) {
let buttonPosition = sender.convertPoint(CGPointZero, toView: self.collectionView)
let indexPath = self.collectionView.indexPathForItemAtPoint(buttonPosition)
////
println(indexPath?.item)
////
let post = self.arrayOfDetails[indexPath!.item]
var alertMessage = NSString(format:"*User: %#\r *Text: %#\r *Created at %#", post.username, post.text, post.CreatedAt)
var reportAlert = UIAlertController(title: "Report Content", message:alertMessage as String, preferredStyle: UIAlertControllerStyle.Alert)
reportAlert.addAction(UIAlertAction(title: "Yes", style: .Default, handler: { (action: UIAlertAction!) in
println("Handle Report Logic here")
var currentUploads = PFObject(className: "banned")
currentUploads["username"] = post.username
currentUploads["imageText"] = post.text
currentUploads["imageFile"] = post.image
currentUploads["identifierForVendor"] = post.deviceID
currentUploads["flaggedBy"] = PFUser.currentUser()?.username
currentUploads["flaggedByUUID"] = UIDevice.currentDevice().identifierForVendor.UUIDString
currentUploads.saveInBackgroundWithBlock({ (success: Bool, error: NSError?) -> Void in
if error == nil{
//**Success saving, now save image.**//
currentUploads.saveInBackgroundWithBlock({ (success: Bool, error: NSError?) -> Void in
if error == nil{
// Take user home
print("Data uploaded")
// Show UIAlertView
let alert = UIAlertView()
alert.title = "Message"
alert.message = "You report has been sent. Thank you for your support."
alert.addButtonWithTitle("Close")
alert.show()
}
else{
print(error)
}
})
}
else{
print(error)
}
})
}))
reportAlert.addAction(UIAlertAction(title: "Cancel", style: .Default, handler: { (action: UIAlertAction!) in
println("Handle Cancel Logic here")
}))
presentViewController(reportAlert, animated: true, completion: nil)
}
You should be keeping a reference to oneobject, or more broadly, all of the objects returned from the query. Now when something changes you can get the appropriate instance from the objects array and then update and save it to modify the existing 'row'.

Why did my app crash when using cloudkit?

When I try to load data from iCloud using Swift, the app crashes with this error. Why is that? I can't seem to find the bug?
Code:
func LoadEvents() {
let predicate:NSPredicate = NSPredicate(value: true)
let query:CKQuery = CKQuery(recordType: "Data", predicate: predicate)
query.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
if let database = self.publicDatabase {
database.performQuery(query, inZoneWithID: nil, completionHandler: { (records:[AnyObject]!, error:NSError!) in
if error != nil {
self.alert("Error: \(error.localizedDescription)", Message: "Make sure iCloud is turned on and you are connected to the internet")
} else {
dispatch_async(dispatch_get_main_queue()) {
self.EventsArray.removeAll(keepCapacity: false)
for record in records {
let usernameRecord:CKRecord = record as CKRecord
self.EventsArray.insert(usernameRecord.objectForKey("Events") as String, atIndex: 0)
println("1")
}
//update data
self.collectionView.reloadData()
}
}
})
}
}
The record that you are reading probably does not have a field named Events. So the objectForKey will return nil which cannot be casted to string. You could solve this by using as? String or use:
if let event = usernameRecord.objectForKey("Events") as? String{
self.EventsArray.insert(event, atIndex: 0)
}

Resources