Cannot fetch multiple CKReference records from public Database in a for loop

71 views Asked by At

I have a contact CKRecord with many location CKRecords ( 1 to many relationship) Both contact CKRecord and Location CKRecord are created in public Database. I add CKReference fro contact to locaiotn via a field named owningContact on location.

ckRecord["owningContact"] = CKReference(record: contactRecord!, action: .deleteSelf)

I go to cloudKit dashboard and verify both the records exist. The location CKRecord has field owningContact that has the recordName of the contact CKRecord. I defined a function to get locations like this:

private func iCloudFetchLocations(withContactCKRecord: CKRecord, completionHandler: @escaping ([CKRecord]?, Error?) -> Void) {
     var records = [CKRecord]()

    let recordToMatch = CKReference(recordID: withContactCKRecord.recordID, action: .deleteSelf)
    let predicate = NSPredicate(format: "owningContact == %@", recordToMatch)
    
    // Create the query object.
    let query = CKQuery(recordType: "location", predicate: predicate)
    let queryOp = CKQueryOperation(query: query)
    queryOp.resultsLimit = 1
    queryOp.qualityOfService = .userInteractive
    queryOp.recordFetchedBlock = {
        records.append($0)
        print($0)
    }
    queryOp.queryCompletionBlock = { (cursor, error) in
        guard error == nil else {
            if let ckerror = error as? CKError {
                
                self.aErrorHandler.handleCkError(ckerror: ckerror)
            }
            return
        }
        if (cursor != nil) {
            let newOperation = CKQueryOperation(cursor: cursor!)
            newOperation.resultsLimit = queryOp.resultsLimit
            newOperation.recordFetchedBlock = queryOp.recordFetchedBlock
            newOperation.queryCompletionBlock = queryOp.queryCompletionBlock
        
            self.publicDB?.add(newOperation)
        }
        completionHandler(records, error)
    }
    self.publicDB?.add(queryOp)
}

Then I call the code to fetch location CKRecord based on contact CKRecord like this:

 let predicate = NSPredicate(format: "TRUEPREDICATE")
    let query = CKQuery(recordType: Cloud.Entity.Contact, predicate: predicate)
    publicDB?.perform(query, inZoneWith: nil, completionHandler: { (records, error) in
        guard error == nil else {
            if let ckerror = error as? CKError {
                self.aErrorHandler.handleCkError(ckerror: ckerror)
            }
            return
            completion(false)
        }
        if let contactRecords = records {
            for aContactRecord in contactRecords {
                // fetch Location Data
                self.iCloudFetchLocations(withContactCKRecord: aContactRecord, completionHandler: { records, error in
                    guard error == nil else {
                        if let ckerror = error as? CKError {
                            self.aErrorHandler.handleCkError(ckerror: ckerror)
                        }
                        return
                            completion(false)
                    }
                    
                    if let locationRecords = records {
              }
         })
     }
  }
 })

I have two contacts the first one has been CKReferenc'ed to the location, where as the second contact is still not yet CKReferenc'ed to the location. I think here is the problem: First time in the loop contact CKRecord information is sent by calling iCloudFetchLocations which returns immediately without waiting for cloud response, and the for loop sends the second contact and calls iCloudFetchLocations again. Since the second contact has no CKReference to the location, the call fails and I can never get to the first contact's location since it hasn't returned yet.

How to fix this?

1

There are 1 answers

0
vrao On BEST ANSWER

I found that I had not set the CKReference field: owningContact as Queryable. The way I found out is printing error like this: 

 if let ckerror = error as? CKError {
            print(ckerror.userInfo)
            print(ckerror.errorUserInfo)
            self.aErrorHandler.handleCkError(ckerror: ckerror)
  }

As soon as I did that it started working, Since I was in a for loop it was timing out on previous fetch I think.