斯威夫特 5
在浏览了一堆关于 SO 的帖子和解决方案之后,我设法找到了一个适合我需求的解决方案,并且对于任何只想从 iCloud 获取给定类型的所有记录的人来说应该足够简单。
解决方案
该解决方案使用 CKDatabase 的扩展来引入一种方法来处理cursor: CKQueryOperation.Cursor
继续CKQueryOperation
向 iCloud 询问更多记录的方法。在这种方法中,我分派到后台队列,这样我就可以阻止它并等待操作完全完成,无论是在收到错误时还是在最后一批记录时。一旦信号量解锁队列,它就会继续调用主完成块并返回结果。我还在完成处理程序中利用了 Swift 的Result
类型。
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))
}
}
}
}
用法
对于任何熟悉 Swift 闭包语法和Result
类型的人来说,使用该方法是相当简单的。
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
}
}
}