GuiSoySauce
GuiSoySauce

Reputation: 1783

CloudKit completion handler and dispatch_sync issue

I have the following query to Cloud Kit

func cloudKitManageUserPublicTable (typeOfOperation: Int, encriptedBabyName: String?, encriptedOllyKey:String?, result: (error: NSError?, userHasBeenFound: Bool?, ollYKeyHasBeenFound: Bool?, encriptedBabyNameFound: String?) -> Void){

    // OPERATION TYPES
    // 1 - search for user and add, link or update a key
    // 2 - search for user and check he has a key
    // 3 - search for a key
    // 4 - search for user and add a default one if has not been found

    print("cloudKitManageUserPublicTable - operation \(typeOfOperation)")
    var recordCounter = 0
    var publicUserRecord = CKRecord(recordType: "PublicUsers")
    let useriCloudID = NSUserDefaults.standardUserDefaults().objectForKey("useriCloudID") as! String

    var predicate = NSPredicate()
    switch typeOfOperation {
    case 1:
        predicate = NSPredicate(format: "useriCloudID == %@", useriCloudID)
    case 2:
        predicate = NSPredicate(format: "useriCloudID == %@", useriCloudID)
    case 3:
        predicate = NSPredicate(format: "encriptedOllyKey == %@", encriptedOllyKey!)
    default:
        print("no scenarios")
    }
    let cloudKitQuery = CKQuery(recordType: "PublicUsers", predicate: predicate)
    let queryOperation = CKQueryOperation(query: cloudKitQuery)
    let operationQueue = NSOperationQueue()
    let publicDatabase = CKContainer.defaultContainer().publicCloudDatabase;
    queryOperation.database = publicDatabase
    queryOperation.recordFetchedBlock = { (record : CKRecord) -> Void in
        publicUserRecord = record
        recordCounter += 1
    }
    queryOperation.queryCompletionBlock = { (cursor: CKQueryCursor?, error: NSError?) -> Void in
            print("cloudKitManageUserPublicTable - # of record found - \(recordCounter)")
            if error != nil
            {
                // ERROR  STOP
                print("cloudKitManageUserPublicTable - error - \(error)")
                result(error: error, userHasBeenFound: nil, ollYKeyHasBeenFound: nil, encriptedBabyNameFound: nil)
            }
            else
            {
                switch typeOfOperation {
                case 1:
                    // KEY FOUND, UPDATE
                    print("cloudKitManageUserPublicTable - search for user and add or update a key")
                    publicUserRecord["encriptedBabyName"] = encriptedBabyName!
                    publicUserRecord["encriptedOllyKey"] = encriptedOllyKey!
                    publicUserRecord["hasKey"] = true
                    publicDatabase.saveRecord(publicUserRecord) { (CKRecord: CKRecord?, error: NSError?) -> Void in
                            if error != nil
                            {
                                print("cloudKitManageUserPublicTable - creating key - UPDATE error \(error)")
                                result(error: error, userHasBeenFound: nil, ollYKeyHasBeenFound: nil, encriptedBabyNameFound: nil)
                            }
                            else
                            {
                                print("cloudKitManageUserPublicTable - creating key - UPDATE OK")
                                result(error: error, userHasBeenFound: nil, ollYKeyHasBeenFound: true, encriptedBabyNameFound: nil)
                            }
                    }

                case 2:
                    print("cloudKitManageUserPublicTable - search for user and check it has a key")
                    if publicUserRecord.objectForKey("hasKey") as? Bool == false
                    {
                        print("cloudKitManageUserPublicTable - user do not have a key")
                        result(error: nil, userHasBeenFound: nil, ollYKeyHasBeenFound: false, encriptedBabyNameFound: nil)
                    }
                    else
                    {
                        print("cloudKitManageUserPublicTable - user has a key")
                        result(error: nil, userHasBeenFound: nil, ollYKeyHasBeenFound: true, encriptedBabyNameFound: nil)
                    }
                case 3:
                    if recordCounter == 0
                    {
                        print("cloudKitManageUserPublicTable - no record has this key")
                        result(error: nil, userHasBeenFound: nil, ollYKeyHasBeenFound: false, encriptedBabyNameFound: nil)
                    }
                    else
                    {
                        print("cloudKitManageUserPublicTable - \(recordCounter) records have this key")
                        result(error: nil, userHasBeenFound: nil, ollYKeyHasBeenFound: true, encriptedBabyNameFound: nil)
                    }
                case 4:
                    if recordCounter == 0
                    {
                        // NO USER FOUND, CREATE
                        print("cloudKitManageUserPublicTable - search for user and add a default one if has not been found")
//                        publicUserRecord["encriptedBabyName"] = ""
//                        publicUserRecord["encriptedOllyKey"] = ""
                        publicUserRecord["hasKey"] = false
                        publicUserRecord["useriCloudID"] = useriCloudID
                        publicDatabase.saveRecord(publicUserRecord) { (CKRecord: CKRecord?, error: NSError?) -> Void in
                            dispatch_async(dispatch_get_main_queue()) {
                                if error != nil
                                {
                                    print("cloudKitManageUserPublicTable - no user - CREATE error \(error)")
                                    result(error: nil, userHasBeenFound: false, ollYKeyHasBeenFound: nil, encriptedBabyNameFound: nil)
                                }
                                else
                                {
                                    print("cloudKitManageUserPublicTable - no user found - CREATE ok")
                                    result(error: nil, userHasBeenFound: false, ollYKeyHasBeenFound: nil, encriptedBabyNameFound: nil)
                                }
                            }
                        }
                    }
                    else
                    {
                        // USER FOUND - DO NOTHING
                        print("cloudKitManageUserPublicTable - user exists, do nothing for now")
                        result(error: nil, userHasBeenFound: true, ollYKeyHasBeenFound: nil, encriptedBabyNameFound: nil)
                    }
                default:
                    print("no scenarios")
                }
            }
    }
    operationQueue.addOperation(queryOperation)
}

The method above is called for the FIRST TIME by the method bellow where I wait for the completion handler to return and then call the method above for the SECOND TIME to then verify if the publicUserRecord["hasKey"] = false was set to false.

But my problem is. When I call the method the SECOND TIME to verify the publicUserRecord["hasKey"] it returns that nothing has been saved yet. If I then wait a bit and call the method to verify the publicUserRecord["hasKey"] for the THIRD TIME then it finds and verifies it.

As I am doing the very same call and the results have been saved in the FIRST call seems that theres a bit of a lag with apple servers or am I not using the completion handlers, dispatch_async, dispatch_sync properly? Any ideas?

  func manageUserPublicTable(){

        tryAgainButtonOutlet.enabled = false
        let spinningActivity = MBProgressHUD.showHUDAddedTo(self.view, animated: true)
        spinningActivity.labelText = "Talking to Apple Servers"
        spinningActivity.detailsLabelText = "Creating user credentials..."

        cloudKitManageUserPublicTable(1) { (error, userExists) -> Void in
            dispatch_sync(dispatch_get_main_queue()) {
                if error != nil
                {
                    spinningActivity.hide(true)
                    // let user try again
                    let optionMenu = UIAlertController(title: nil, message: "Are you all set to upload this record?!", preferredStyle: .ActionSheet)
                    let tryAgain = UIAlertAction(title: "Try again", style: .Default, handler: {
                        (alert: UIAlertAction!) -> Void in
                        self.tryAgainButtonOutlet.enabled = true
                    })
                    let cancelAction = UIAlertAction(title: "Not yet...", style: .Cancel, handler: {
                        (alert: UIAlertAction!) -> Void in
                        self.tryAgainButtonOutlet.enabled = true
                    })
                    optionMenu.addAction(tryAgain)
                    optionMenu.addAction(cancelAction)
                    if(isIPad()) {
                        optionMenu.popoverPresentationController!.permittedArrowDirections = UIPopoverArrowDirection()
                        optionMenu.popoverPresentationController!.sourceView = self.view
                        optionMenu.popoverPresentationController!.sourceRect = CGRectMake(self.view.bounds.size.width / 2.0, self.view.bounds.size.height / 2.0, 1.0, 1.0)
                    }
                    self.presentViewController(optionMenu, animated: true, completion: nil)
                }
                else
                {
                    spinningActivity.hide(true)
                    self.manageEncriptedKey()
                }
            }
        }

    }

Upvotes: 1

Views: 194

Answers (1)

Edwin Vermeer
Edwin Vermeer

Reputation: 13127

Indeed there can be some time between saving data and being able to retrieve it. There is no specification how long this could be. Usually it's less than seconds.

So you have to make your app logic so that it does not care about that. You already saved the record so your app already knows the content of that record. You could pass on the record from the callback so that you don't have to query for it again.

One other think. Your functions are a bit large to my taste. There is too much functionality in 1 function. That makes it difficult to read. Ideally a function should do only one thing.

Upvotes: 1

Related Questions