Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CKQueryOperation queryCompletionBlock only runs 3 times

I'm trying to download a batch of records from my iCloud public database using CloudKit and the cursor. The code works fine for the first 3 executions, regardless of how the resultsLimit is set, but the 4th completion block is never executed. If resultsLimit is not set I get 300 records, if it's set to 50 I get 150, if it's set to 5 I get 15...

No error is reported and the block appears to be added but is never executed. Platform is OS X. Here's the code in question:

        let queryOp = CKQueryOperation(query: query)
        queryOp.recordFetchedBlock = self.fetchedDetailRecord
        queryOp.resultsLimit = 5
        queryOp.queryCompletionBlock = { [weak self]
          (cursor: CKQueryCursor!, error: NSError!) -> Void in
          println("comp block called with \(cursor) \(error)")
          if error != nil {
            println("Error on fetch \(error.userInfo)")
          } else {
            if cursor != nil {
              let nextOp = CKQueryOperation(cursor: cursor)
              nextOp.recordFetchedBlock = self!.fetchedDetailRecord
              nextOp.queryCompletionBlock = queryOp.queryCompletionBlock
              nextOp.resultsLimit = 5
              self!.publicDatabase?.addOperation(nextOp)
              println("added next fetch")
            } else {
              self!.fileHandle!.closeFile()
              self!.exportProgressIndicator.stopAnimation(self)
            }
          }
        }

        self.publicDatabase?.addOperation(queryOp)

And here's the results from the console -

459013587628.012 0 459013587628.621 1 459013587628.863 2 459013587629.113 3 459013587629.339 4 comp block called with nil added next fetch 459013587828.552 5 459013587828.954 6 459013587829.198 7 459013587829.421 8 459013587829.611 9 comp block called with nil added next fetch 459013587997.084 10 459013587997.479 11 459013587997.74 12 459013587997.98 13 459013587998.207 14

The big number is just the time between calls to the recordFetchedBlock with the second number being the count of times that block has been called.

I'm stumped...any ideas on how to proceed? Oh, container and publicDatabase are class variables and initialized prior to running the code snippet above.

like image 739
Oswaldt Avatar asked Jul 19 '15 16:07

Oswaldt


2 Answers

The way you're defining nextOp inside of your queryCompletionBlock scope is causing a problem after three iterations of that block. If you break the creation of the CKQueryOperation out into its own method your problem goes away.

Here is the code:

func fetchedDetailRecord(record: CKRecord!) -> Void {
    println("Retrieved record: \(record)")
}

func createQueryOperation(cursor: CKQueryCursor? = nil) -> CKQueryOperation {
    var operation:CKQueryOperation
    if (cursor != nil) {
        operation = CKQueryOperation(cursor: cursor!)
    } else {
        operation = CKQueryOperation(query: CKQuery(...))
    }
    operation.recordFetchedBlock = self.fetchedDetailRecord
    operation.resultsLimit = 5
    operation.queryCompletionBlock = { [weak self]
        (cursor: CKQueryCursor!, error: NSError!) -> Void in
        println("comp block called with \(cursor) \(error)")
        if error != nil {
            println("Error on fetch \(error.userInfo)")
        } else {
            if cursor != nil {
                self!.publicDatabase?.addOperation(self!.createQueryOperation(cursor: cursor))
                println("added next fetch")
            } else {
                self!.fileHandle!.closeFile()
                self!.exportProgressIndicator.stopAnimation(self) 
            }
        }
    }
    return operation
}

func doQuery() {
    println("Querying records...")
    self.publicDatabase?.addOperation(createQueryOperation())
}
like image 62
Dave Browning Avatar answered Nov 11 '22 15:11

Dave Browning


This could be another approach (already updated to Swift 3).

It does not stops after third iteration, but continues until the end

var queryOp = CKQueryOperation(query: query)
queryOp.recordFetchedBlock = self.fetchedARecord
queryOp.resultsLimit = 5
queryOp.queryCompletionBlock = { [weak self] (cursor : CKQueryCursor?, error : Error?) -> Void in
    print("comp block called with \(cursor) \(error)")

    if cursor != nil {
        let nextOp = CKQueryOperation(cursor: cursor!)
        nextOp.recordFetchedBlock = self!.fetchedARecord
        nextOp.queryCompletionBlock = queryOp.queryCompletionBlock
        nextOp.resultsLimit = queryOp.resultsLimit

        //this is VERY important
        queryOp = nextOp

        self!.publicDB.add(queryOp)
        print("added next fetch")
    }

}

self.publicDB.add(queryOp)

I successfully use it to retrieve hundreds of records from CloudKit

like image 31
carmine Avatar answered Nov 11 '22 14:11

carmine