CloudKit private database returns first 100 CKRecords
Ok, i found a solution. See below:
func loadDataFromCloudKit() {
var results: [AnyObject] = []
let cloudContainer = CKContainer.defaultContainer()
let privateDatabase = cloudContainer.privateCloudDatabase
let predicate = NSPredicate(value: true)
let query = CKQuery(recordType: "Data", predicate: predicate)
query.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: true)]
let queryOperation = CKQueryOperation(query: query)
queryOperation.desiredKeys = ["id","name"]
queryOperation.queuePriority = .VeryHigh
// Max limit is still 100
queryOperation.resultsLimit = 100
queryOperation.recordFetchedBlock = { (record:CKRecord!) -> Void in
results.append(record)
}
queryOperation.queryCompletionBlock = { (cursor, error) in
dispatch_async(dispatch_get_main_queue()) {
if (error != nil) {
print("Failed to get data from iCloud - \(error!.localizedDescription)")
} else {
print("Successfully retrieve the data form iCloud")
}
}
// see cursor here, if it is nil than you have no more records
// if it has a value than you have more records to get
if cursor != nil {
print("there is more data to fetch")
let newOperation = CKQueryOperation(cursor: cursor!)
newOperation.recordFetchedBlock = { (record:CKRecord!) -> Void in
results.append(record)
}
newOperation.queryCompletionBlock = queryOperation.queryCompletionBlock
privateDatabase.addOperation(newOperation)
} else {
// gets more then 100
print(results.count)
}
}
privateDatabase.addOperation(queryOperation)
}
CloudKit Query Operation only returns 300 results
Could you try setting:
queryOperation.qualityOfService = .UserInitiated
This will indicate that your user interaction requires the data.
Otherwise it could happen that de request is ignored completely.
As discussed below the actual answer was that you should not re-use completion blocks. Instead you should create a recursive function for fetching the next records from a cursor. A sample of that can be found at: EVCloudKitDao
Why will my CKQueryOperation only return a Cursor if the results limit is less than 1000?
I believe that 400 is the the limit for a single operation, so you need to use cursor to get more records, and keep on doing that while returned cursor is not nil
.
See how it is done in RxCloudKit library' RecordFetcher
-
https://github.com/maxvol/RxCloudKit/blob/master/RxCloudKit/RecordFetcher.swift
Swift CloudKit and CKQuery: how to iteratively retrieve records when queryResultBlock returns a query cursor
No need for queryResultBlock
in Swift 5.5.
I use this because my CKRecord
types are always named the same as their Swift counterparts. You can replace recordType: "\(Record.self)"
with your recordType
if you want, instead.
public extension CKDatabase {
/// Request `CKRecord`s that correspond to a Swift type.
///
/// - Parameters:
/// - recordType: Its name has to be the same in your code, and in CloudKit.
/// - predicate: for the `CKQuery`
func records<Record>(
type _: Record.Type,
zoneID: CKRecordZone.ID? = nil,
predicate: NSPredicate = .init(value: true)
) async throws -> [CKRecord] {
try await withThrowingTaskGroup(of: [CKRecord].self) { group in
func process(
_ records: (
matchResults: [(CKRecord.ID, Result<CKRecord, Error>)],
queryCursor: CKQueryOperation.Cursor?
)
) async throws {
group.addTask {
try records.matchResults.map { try $1.get() }
}
if let cursor = records.queryCursor {
try await process(self.records(continuingMatchFrom: cursor))
}
}
try await process(
records(
matching: .init(
recordType: "\(Record.self)",
predicate: predicate
),
inZoneWith: zoneID
)
)
return try await group.reduce(into: [], +=)
}
}
}
CloudKit returns too many records
I just found out, that there is a bug in CloudKit, that deleted records are somehow hidden still available. I resetted my development environment and imported the data again and I got the correct amount of records returned.
More information can be found in this Stackoverflow post:
Deleted CloudKit records Reappear
Can I receive count of records corresponding to CKQuery from CloudKit?
Aggregation queries are not possible in CloudKit. So you have to query all records and count those. To make sure that all records will be returned, you have to set operation.resultsLimit to a value larger than the count otherwise it could happen that not all records are returned.
Related Topics
Constant Movement in Spritekit
React-Native iOS Podfile Issue with "Use_Native_Modules!"
Corebluetooth: What Is the Lifetime of Unique Uuids
Sprite Kit Pin Joints Appear to Have an Incorrect Anchor
Objective-C Wrapper for Cfunctionpointer to a Swift Closure
Uiscrollview Pauses Nstimer While Scrolling
How to Get Index Path of Cell on Switch Change Event in Section Based Table View
How to Install an Unsigned IPA File on My Device Using an Apple Developer Account
Setting Multiple Times for Notifications in Swift
How to Render View into Image Faster
Get Pixel Value from Cvpixelbufferref in Swift
Go VS. Return Button in iOS Keyboard for HTML Input Forms
Avfoundation Image Orientation Off by 90 Degrees in the Preview But Fine in Camera Roll
Uisearchdisplaycontroller Without Dimming
How to Detect iOS 6 and All Minor Versions by User Agent
Play Audio from Internet Using Avaudioplayer