0

My function runsQueries runs two different queries. And i need these two queries to be completed before I can call updateResults function.

What is the best approach to get it done? I've tried a few different things but nothing actually worked so far.

    func runsQueries(){

    var foundRecords = [CKRecords]()

    let notified = dispatch_semaphore_create(0)
    let group = dispatch_group_create()
    let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)

    dispatch_group_async(group, queue) {
        // query 1
        let predicate1 = NSPredicate(format: "userID = %@", user1ID)
        let cloudKitQuery1 = CKQuery(recordType: "Messages", predicate: predicate1)
        publicDatabase.performQuery(cloudKitQuery1, inZoneWithID: nil) { (messageRecords: [CKRecord]?, error: NSError?) in
            if error != nil
            {
                    print("-> cloudKitLoadMessage - userID1 error \(error)")
            }
            else
            {
                     print("-> cloudKitLoadMessage - user1Done - message") 
                     foundRecords.apend(messageRecords[0])
            }
        }

        // query 2
        let predicate2 = NSPredicate(format: "userID = %@", user2ID)
        let cloudKitQuery2 = CKQuery(recordType: "Messages", predicate: predicate2)
        publicDatabase.performQuery(cloudKitQuery2, inZoneWithID: nil) { (messageRecords: [CKRecord]?, error: NSError?) in
            if error != nil
            {
                    print("-> cloudKitLoadMessage - userID2 error \(error)")
            }
            else
            {
                     print("-> cloudKitLoadMessage - user2Done - message")
                     foundRecords.apend(messageRecords[0])  
            }
        }
    }

    dispatch_group_notify(group, queue) {
            // This block will be executed when all tasks are complete
          print("All tasks complete")
          dispatch_semaphore_signal(notified)
    }

    dispatch_group_wait(group, DISPATCH_TIME_FOREVER)
    dispatch_semaphore_wait(notified, DISPATCH_TIME_FOREVER)
    print("Semaphore done")

        // only call updateResults when queries 1 and 2 are done
        updateResults(foundRecords)

   }

Second function

func updateResults(messageRecords: [CKrecord]){

  // do something now that you got both messages  

}

Based some ideas from: https://gist.github.com/nbhasin2/735cd80298b5d47852f2

GuiSoySauce
  • 1,763
  • 3
  • 24
  • 37

4 Answers4

2

Use "Dispatch Gruop" and put the two query in two different dispatch block.

See this answer: https://stackoverflow.com/questions/11909629/waiting-until-two-async-blocks-are-executed-before-starting-another-block

Community
  • 1
  • 1
christian mini
  • 1,662
  • 20
  • 39
1

There may be more elegant solutions out there but I've used this pattern a good bit:

     func runsQueries(){

        var foundRecords = [CKRecords]()

        var query1Finished = false
        var query2Finished = false

        let updateResultsIfNeeded {
            if query1Finished && query2Finished {
                updateResults(foundRecords)
            }
        }

        // query 1
        let predicate1 = NSPredicate(format: "userID = %@", user1ID)
        let cloudKitQuery1 = CKQuery(recordType: "Messages", predicate: predicate1)
        publicDatabase.performQuery(cloudKitQuery1, inZoneWithID: nil) { (messageRecords: [CKRecord]?, error: NSError?) in
            if error != nil
            {
                print("-> cloudKitLoadMessage - userID1 error \(error)")
            }
            else
            {
                print("-> cloudKitLoadMessage - user1Done - message")
                foundRecords.apend(messageRecords[0])
            }

            query1Finished = true
            updateResultsIfNeeded()
        }

        // query 2
        let predicate2 = NSPredicate(format: "userID = %@", user2ID)
        let cloudKitQuery2 = CKQuery(recordType: "Messages", predicate: predicate2)
        publicDatabase.performQuery(cloudKitQuery2, inZoneWithID: nil) { (messageRecords: [CKRecord]?, error: NSError?) in
            if error != nil
            {
                print("-> cloudKitLoadMessage - userID2 error \(error)")
            }
            else
            {
                print("-> cloudKitLoadMessage - user2Done - message")
                foundRecords.apend(messageRecords[0])
            }

            query2Finished = true
            updateResultsIfNeeded()
        }
    }
Richmond Watkins
  • 1,342
  • 9
  • 17
1

Base on @christian mini's answer:

dispatch_group_t group = dispatch_group_create();

dispatch_group_async(group,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
        // query 1
    let predicate1 = NSPredicate(format: "userID = %@", user1ID)
    let cloudKitQuery1 = CKQuery(recordType: "Messages", predicate: predicate1)
    publicDatabase.performQuery(cloudKitQuery1, inZoneWithID: nil) { (messageRecords: [CKRecord]?, error: NSError?) in
        if error != nil
        {
                print("-> cloudKitLoadMessage - userID1 error \(error)")
        }
        else
        {
                 print("-> cloudKitLoadMessage - user1Done - message") 
                 foundRecords.apend(messageRecords[0])
        }
    }
});


dispatch_group_async(group,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
        // query 2
    let predicate2 = NSPredicate(format: "userID = %@", user2ID)
    let cloudKitQuery2 = CKQuery(recordType: "Messages", predicate: predicate2)
    publicDatabase.performQuery(cloudKitQuery2, inZoneWithID: nil) { (messageRecords: [CKRecord]?, error: NSError?) in
        if error != nil
        {
                print("-> cloudKitLoadMessage - userID2 error \(error)")
        }
        else
        {
                 print("-> cloudKitLoadMessage - user2Done - message")
                 foundRecords.apend(messageRecords[0])  
        }
    }
 });

dispatch_group_notify(group,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
        // only call updateResults when queries 1 and 2 are done
    updateResults(foundRecords) 
}); 

Anyway, you should separate the query block code to a function. Your code is duplicated.

tuledev
  • 10,177
  • 4
  • 29
  • 49
0

Thanks for all the answers. I had to end-up mixing a few things to get it working thought it would be worth sharing here.

I had to add dispatch_group_enter and dispatch_group_leave to get it to stop while running cloudkit queries.

It is weird because it worked without dispatch_group_enter and dispatch_group_leave if it was running just the code bellow:

let timeInterval = Double(arc4random_uniform(1000)) * 0.01
        NSThread.sleepForTimeInterval(timeInterval)

Also added a for loop to run up to 4 queries. Tried to make a for with an array of IDs and get an unlimited number of queries but it put the IDs out of order and I needed them in order. May try it with a dictionary.

let notified = dispatch_semaphore_create(0)
let group = dispatch_group_create()
let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)

for n in 1..<5 {

    dispatch_group_async(group, queue) {
        dispatch_group_enter(group)

        print("nnnn \(n)")

        var userID = String()
        switch n {
        case 1:
            userID = user1ID
        case 2:
            userID = user2ID
        case 3:
            userID = user3ID
        case 4:
            userID = user4ID
        default:
            print("")
        }

        var predicate = NSPredicate()
        predicate = NSPredicate(format: "userID = %@ AND chatRoomReference = %@", userID, roomReference)
        let cloudKitQuery = CKQuery(recordType: "Messages", predicate: predicate)
        let sort = NSSortDescriptor(key: "date", ascending: false)
        cloudKitQuery.sortDescriptors = [sort]
        publicDatabase.performQuery(cloudKitQuery, inZoneWithID: nil) { (messageRecords: [CKRecord]?, error: NSError?) in

            // result in here

        switch n {
        case 1:
            user1Dic = userDic
        case 2:
            user2Dic = userDic
        case 3:
            user3Dic = userDic
        case 4:
            user4Dic = userDic
        default:
            print("")
        }
            dispatch_group_leave(group)
        }
    }
}

dispatch_group_notify(group, queue) {
    print("All tasks complete")
    dispatch_semaphore_signal(notified)
}

dispatch_group_wait(group, DISPATCH_TIME_FOREVER)
print("All tasks dispatch_group_wait")

dispatch_semaphore_wait(notified, DISPATCH_TIME_FOREVER)
print("All tasks dispatch_semaphore_wait")

// that is when I return the results from all queries
updateResults(error: nil, user1Dic: user1Dic, user2Dic: user2Dic, user3Dic: user3Dic, user4Dic: user4Dic)
GuiSoySauce
  • 1,763
  • 3
  • 24
  • 37