Usage of CKQueryOperation.recordMatchedBlock and .queryResultBlock

Anyone know the correct usage for CKQueryOperation.recordMatchedBlock and .queryResultBlock?

Xcode is telling me to use them as the old .recordFetchedBlock and .queryCompletionBlock are deprecated, but there isn't any documentation to say how they should be used. ChatGPT, Bard etc aren't able to provide a correct answer either:

specifically, I need to convert the following to use those methods:

operation.recordFetchedBlock = { record in
            // Convert CKRecord to Core Data object and save to Core Data
            self.saveToCoreData(record: record)
        }
operation.queryCompletionBlock = { (cursor, error) in
            // Handle potential errors
            if let error = error {
                print("Error syncing first of month records: \(error)")
            }
            
            if let cursor = cursor {
                // There are more records to fetch for this category, continue fetching
                self.continueSyncing(cursor: cursor, completion: completion)
            } else {
                // Done syncing this category, move to the next sync task
                completion()
            }
}
Answered by Scott in 762309022

For the record block, let’s compare their signatures (adding some handy names and spacing for clarity):

typealias OldRecordFetchedBlock = (                    CKRecord        ) -> Void
typealias NewRecordMatchedBlock = (CKRecord.ID, Result<CKRecord, Error>) -> Void

The main change is the CKRecord is now wrapped in a Result, so it can indicate that either a record was successfully fetched, or if an error occurred. You just need to unpack the record from the Result in order to use it:

operation.recordMatchedBlock = { id, result in
    switch result {
    case .success(let record):
        self.saveToCoreData(record: record)
    case .failure(let error):
        // handle the error in some way
    }
}

The change from queryCompletionBlock to queryResultBlock is similar. The cursor and error are now wrapped in a Result rather than being separate parameters.

Accepted Answer

For the record block, let’s compare their signatures (adding some handy names and spacing for clarity):

typealias OldRecordFetchedBlock = (                    CKRecord        ) -> Void
typealias NewRecordMatchedBlock = (CKRecord.ID, Result<CKRecord, Error>) -> Void

The main change is the CKRecord is now wrapped in a Result, so it can indicate that either a record was successfully fetched, or if an error occurred. You just need to unpack the record from the Result in order to use it:

operation.recordMatchedBlock = { id, result in
    switch result {
    case .success(let record):
        self.saveToCoreData(record: record)
    case .failure(let error):
        // handle the error in some way
    }
}

The change from queryCompletionBlock to queryResultBlock is similar. The cursor and error are now wrapped in a Result rather than being separate parameters.

Usage of CKQueryOperation.recordMatchedBlock and .queryResultBlock
 
 
Q