I have currently got CloudKit set up in my app so that I am adding a new record using the help of the following code below,
CKRecordID *recordID
Swift 5
After looking through a bunch of posts and solutions on SO I have managed to come with a solution that suits my needs and should be simple enough for anyone that just wants to fetch all of their records of given type from iCloud.
The solution that uses an extension to the CKDatabase to introduce a method that handles the cursor: CKQueryOperation.Cursor
of CKQueryOperation
to continue asking iCloud for more records. In this approach I dispatch to the background queue so I can block it and wait for the operation to be finished completely, either on receiving an error or with the last batch of records. Once the semaphore unlocks the queue it proceeds with calling the main completion block with the result. Also I am taking advantage of Swift's Result
type in the completion handler.
extension CKDatabase {
func fetchAll(
recordType: String, resultsLimit: Int = 100, timeout: TimeInterval = 60,
completion: @escaping (Result<[CKRecord], Error>) -> Void
) {
DispatchQueue.global().async { [unowned self] in
let query = CKQuery(
recordType: recordType, predicate: NSPredicate(value: true)
)
let semaphore = DispatchSemaphore(value: 0)
var records = [CKRecord]()
var error: Error?
var operation = CKQueryOperation(query: query)
operation.resultsLimit = resultsLimit
operation.recordFetchedBlock = { records.append($0) }
operation.queryCompletionBlock = { (cursor, err) in
guard err == nil, let cursor = cursor else {
error = err
semaphore.signal()
return
}
let newOperation = CKQueryOperation(cursor: cursor)
newOperation.resultsLimit = operation.resultsLimit
newOperation.recordFetchedBlock = operation.recordFetchedBlock
newOperation.queryCompletionBlock = operation.queryCompletionBlock
operation = newOperation
self?.add(newOperation)
}
self?.add(operation)
_ = semaphore.wait(timeout: .now() + 60)
if let error = error {
completion(.failure(error))
} else {
completion(.success(records))
}
}
}
}
Using the method is fairly straight forward for anyone familiar with Swift's closure syntax and Result
type.
let database: CKDatabase = ...
database.fetchAll(recordType: "User") { result in
switch result {
case .success(let users):
// Handle fetched users, ex. save them to the database
case .failure(let error):
// Handle Error
}
}
}