CloudKit: Fetch all records with a certain record type?

后端 未结 6 836
鱼传尺愫
鱼传尺愫 2021-02-05 19:50

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         


        
6条回答
  •  [愿得一人]
    2021-02-05 20:10

    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.

    Solution

    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))
                }
            }
        }
    
    }
    

    Usage

    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
            }
        }
    }
    

提交回复
热议问题