Search code examples
iosswifticloudcloudkit

Public cloud database query doesn't return nil when it should


I want to check for the existence of a record with a certain predicate and, if it doesn't exist, do something:

let publicDatabase = CKContainer.default().publicCloudDatabase
let predicate: NSPredicate!
predicate = NSPredicate(format: "username == %@", usernameText)
let query =  CKQuery(recordType: "user", predicate: predicate)
let configuration = CKQueryOperation.Configuration()
configuration.allowsCellularAccess = true
configuration.qualityOfService = .userInitiated

let queryOperation = CKQueryOperation(query: query)
queryOperation.desiredKeys = ["username"]
queryOperation.queuePriority = .veryHigh
queryOperation.configuration = configuration
queryOperation.resultsLimit = 1
queryOperation.recordFetchedBlock = { (record: CKRecord?) -> Void in
    if let record = record {
    // #1
        print("record \(record)")
    } else {
    // #2
        print("none exists")
    }
}

queryOperation.queryCompletionBlock = { (cursor: CKQueryOperation.Cursor?, error: Error?) -> Void in
    if let error = error {
        print("\(error)")
        return
    }
    
    if let cursor = cursor {
        print("cursor: \(cursor)")
    }
}

publicDatabase.add(queryOperation)

When a record matching the predicate exists, the record is returned as it should, but when it doesn't exist, not even nil gets returned for me to react accordingly. What I mean is ideally I want to execute my code in #2 in response to the nonexistence of any records, but recordFetchedBlock doesn't seem to run in that case.


Solution

  • The issue here is recordFetchedBlock is only called when you get a record. No record? Then it won't be called. The below approach should do the trick for you:

    //define an array to store all records
    var allRecords = []
    
    queryOperation.recordFetchedBlock = { record in
        //called once for each record
        //if no results, then it is never called
        //if you get a record, add to the array
        allRecords.append[record]
    }
    
    //the query completion block is called at the end of the query (or when all results can't be returned in one block, then called with non-nil cursor value.  Considering that out of scope for this answer.
    queryOperation.queryCompletionBlock = { (cursor: CKQueryOperation.Cursor?, error: Error?) in
        if let error = error {
            //handle error
        }
        if let cursor = cursor {
           //handle cursor if exists    
        } 
        if allRecords.count == 0 {
            //my query returned no results
            //take desired action    
        }
    }