来自私有区域 returns 的 CKQuery 仅来自 CloudKit 中的前 100 个 CKRecords
CKQuery from private zone returns only first 100 CKRecords from in CloudKit
Cloudkit私有默认区域的查询结果是否有限制?我不知道为什么我只收到以下查询的前 100 条记录:
let p = NSPredicate(format: "(type == 'entered') AND (timestamp >= %@) AND (timestamp <= %@)", from, to)
let q = CKQuery(recordType: self.beaconRecordType, predicate: p)
q.sortDescriptors = [NSSortDescriptor(key: "timestamp", ascending: true)]
self.privateDatabase?.performQuery(q, inZoneWithID: nil, completionHandler: { results, error in
//count = 100
println(results.count)
}
好的。正如埃德温在回答中提到的那样,解决方案是使用 CKQueryOperation 获取初始数据块,然后使用 completionBlock 中的 "cursor" 来触发另一个操作。这是一个例子:
更新
func fetchBeacons(from:NSDate, to:NSDate) {
let p = NSPredicate(value: true)
let q = CKQuery(recordType: self.beaconRecordType, predicate: p)
let queryOperation = CKQueryOperation(query: q)
queryOperation.recordFetchedBlock = fetchedARecord
queryOperation.queryCompletionBlock = { [weak self] (cursor : CKQueryCursor!, error : NSError!) in
if cursor != nil {
println("there is more data to fetch")
let newOperation = CKQueryOperation(cursor: cursor)
newOperation.recordFetchedBlock = self!.fetchedARecord
newOperation.queryCompletionBlock = queryOperation.queryCompletionBlock
self!.privateDatabase?.addOperation(newOperation)
}
}
privateDatabase?.addOperation(queryOperation)
}
var i = 0
func fetchedARecord (record: CKRecord!) {
println("\(NSDate().timeIntervalSinceReferenceDate*1000) \(++i)")
}
100 是标准查询的默认限制。这个数额不是固定的。它可能因 iCloud 总负载而异。如果你想影响那个数量,那么你需要使用 CKQueryOperation 并像这样设置 resultsLimit:
operation.resultsLimit = CKQueryOperationMaximumResults;
CKQueryOperationMaximumResults 是默认值,并将其限制为 100(大部分时间)。不要将该值设置得太高。如果你想要更多的记录,那么使用queryCompletionBlock的游标继续读取更多的记录。
我在我的项目中使用此代码从记录类型中获取所有记录,它在 objective c 中。我使用 "Entry" 作为 desiredKeys.
+ (void)fetchRecordsWithType:(NSString *)recordType
completionHandler:(void (^)(NSArray *records, NSError *error))completionHandler {
NSPredicate *truePredicate = [NSPredicate predicateWithValue:YES];
CKQuery *query = [[CKQuery alloc] initWithRecordType:recordType
predicate:truePredicate];
CKQueryOperation *queryOperation = [[CKQueryOperation alloc] initWithQuery:query];
queryOperation.desiredKeys = @[@"Entry"];
NSMutableArray *results = [NSMutableArray new];
queryOperation.recordFetchedBlock = ^(CKRecord *record) {
[results addObject:record]; };
queryOperation.queryCompletionBlock = ^(CKQueryCursor *cursor, NSError *error) {
[self retrieveNextBatchOfQueryFromCursor:cursor
results:results
error:error
completionHandler:completionHandler]; };
[[self CloudKitContainer].privateCloudDatabase addOperation:queryOperation]; }
+ (void)retrieveNextBatchOfQueryFromCursor:(CKQueryCursor *)cursor
results:(NSMutableArray *)results
error:(NSError *)error
completionHandler:(void (^)(NSArray *records, NSError *error))completionHandler {
// CloudKit apparently has query limit
if (cursor != nil
&& !error) {
CKQueryOperation *nextOperation = [[CKQueryOperation alloc] initWithCursor:cursor];
nextOperation.recordFetchedBlock = ^(CKRecord *record) {
[results addObject:record]; };
nextOperation.queryCompletionBlock = ^(CKQueryCursor *cursor, NSError *error) {
[self retrieveNextBatchOfQueryFromCursor:cursor
results:results
error:error
completionHandler:completionHandler]; };
[[self CloudKitContainer].privateCloudDatabase addOperation:nextOperation]; }
else {
dispatch_async(dispatch_get_main_queue(), ^(void){
completionHandler(results, error); }); }}
另一种 运行 它在一个函数中的方法,它有一个完成处理程序,在获取所有记录之前不会停止。这可以由应用程序中不同的不同视图控制器重复使用。
查询
func cloudKitLoadRecords(result: (objects: [CKRecord]?, error: NSError?) -> Void){
// predicate
var predicate = NSPredicate(value: true)
// query
let cloudKitQuery = CKQuery(recordType: "ClassName", predicate: predicate)
// records to store
var records = [CKRecord]()
//operation basis
let publicDatabase = CKContainer.defaultContainer().publicCloudDatabase
// recurrent operations function
var recurrentOperationsCounter = 101
func recurrentOperations(cursor: CKQueryCursor?){
let recurrentOperation = CKQueryOperation(cursor: cursor!)
recurrentOperation.recordFetchedBlock = { (record:CKRecord!) -> Void in
print("-> cloudKitLoadRecords - recurrentOperations - fetch \(recurrentOperationsCounter++)")
records.append(record)
}
recurrentOperation.queryCompletionBlock = { (cursor:CKQueryCursor?, error:NSError?) -> Void in
if ((error) != nil)
{
print("-> cloudKitLoadRecords - recurrentOperations - error - \(error)")
result(objects: nil, error: error)
}
else
{
if cursor != nil
{
print("-> cloudKitLoadRecords - recurrentOperations - records \(records.count) - cursor \(cursor!.description)")
recurrentOperations(cursor!)
}
else
{
print("-> cloudKitLoadRecords - recurrentOperations - records \(records.count) - cursor nil - done")
result(objects: records, error: nil)
}
}
}
publicDatabase.addOperation(recurrentOperation)
}
// initial operation
var initialOperationCounter = 1
let initialOperation = CKQueryOperation(query: cloudKitQuery)
initialOperation.recordFetchedBlock = { (record:CKRecord!) -> Void in
print("-> cloudKitLoadRecords - initialOperation - fetch \(initialOperationCounter++)")
records.append(record)
}
initialOperation.queryCompletionBlock = { (cursor:CKQueryCursor?, error:NSError?) -> Void in
if ((error) != nil)
{
print("-> cloudKitLoadRecords - initialOperation - error - \(error)")
result(objects: nil, error: error)
}
else
{
if cursor != nil
{
print("-> cloudKitLoadRecords - initialOperation - records \(records.count) - cursor \(cursor!.description)")
recurrentOperations(cursor!)
}
else
{
print("-> cloudKitLoadRecords - initialOperation - records \(records.count) - cursor nil - done")
result(objects: records, error: nil)
}
}
}
publicDatabase.addOperation(initialOperation)
}
用法
cloudKitLoadRecords() { (queryObjects, error) -> Void in
dispatch_async(dispatch_get_main_queue()) {
if error != nil
{
// handle error
}
else
{
// clean objects array if you need to
self.objects.removeAll()
if queryObjects!.count == 0
{
// do nothing
}
else
{
// attach found objects to your object array
self.objects = queryObjects!
}
}
}
}
Swift最简单的例子:
func fetchServices(completion: ErrorHandler? = nil) {
var records = [CKRecord]()
let query = CKQuery(recordType: "Service", predicate: NSPredicate(value: true))
let queryOperation = CKQueryOperation(query: query)
queryOperation.recordFetchedBlock = { record in
records.append(record)
}
queryOperation.queryCompletionBlock = { cursor, error in
self.fetchServices(with: cursor, error: error, records: records, completion: completion)
}
database.add(queryOperation)
}
private func fetchServices(with cursor: CKQueryCursor?, error: Swift.Error?, records: [CKRecord], completion: ErrorHandler? = nil) {
var currentRecords = records
if let cursor = cursor, error == nil {
let queryOperation = CKQueryOperation(cursor: cursor)
queryOperation.recordFetchedBlock = { record in
currentRecords.append(record)
}
queryOperation.queryCompletionBlock = { cursor, error in
self.fetchServices(with: cursor, error: error, records: currentRecords, completion: completion)
}
database.add(queryOperation)
} else {
parseAndSaveServices(with: records, completion: completion)
}
}
我已经在 Swift 4.2 中更新了 GuiSoySauce 的代码。
func cloudKitLoadRecords(result: @escaping (_ objects: [CKRecord]?, _ error: Error?) -> Void) {
// predicate
var predicate = NSPredicate(value: true)
// query
let cloudKitQuery = CKQuery(recordType: "recordType", predicate: predicate)
// records to store
var records = [CKRecord]()
//operation basis
let publicDatabase = CKContainer.defaultContainer().publicCloudDatabase
// recurrent operations function
var recurrentOperationsCounter = 101
func recurrentOperations(cursor: CKQueryCursor?){
let recurrentOperation = CKQueryOperation(cursor: cursor!)
recurrentOperation.recordFetchedBlock = { (record:CKRecord!) -> Void in
print("-> cloudKitLoadRecords - recurrentOperations - fetch \(recurrentOperationsCounter)")
recurrentOperationsCounter += 1
records.append(record)
}
recurrentOperation.queryCompletionBlock = { (cursor: CKQueryOperation.Cursor?, error: Error?) -> Void in
if ((error) != nil) {
print("-> cloudKitLoadRecords - recurrentOperations - error - \(String(describing: error))")
result(nil, error)
} else {
if cursor != nil {
print("-> cloudKitLoadRecords - recurrentOperations - records \(records.count) - cursor \(cursor!.description)")
recurrentOperations(cursor: cursor!)
} else {
print("-> cloudKitLoadRecords - recurrentOperations - records \(records.count) - cursor nil - done")
result(records, nil)
}
}
}
publicDatabase.add(recurrentOperation)
}
// initial operation
var initialOperationCounter = 1
let initialOperation = CKQueryOperation(query: cloudKitQuery)
initialOperation.recordFetchedBlock = { (record:CKRecord!) -> Void in
print("-> cloudKitLoadRecords - initialOperation - fetch \(initialOperationCounter)")
initialOperationCounter += 1
records.append(record)
}
initialOperation.queryCompletionBlock = { (cursor: CKQueryOperation.Cursor?, error: Error?) -> Void in
if ((error) != nil) {
print("-> cloudKitLoadRecords - initialOperation - error - \(String(describing: error))")
result(nil, error)
} else {
if cursor != nil {
print("-> cloudKitLoadRecords - initialOperation - records \(records.count) - cursor \(cursor!.description)")
recurrentOperations(cursor: cursor!)
} else {
print("-> cloudKitLoadRecords - initialOperation - records \(records.count) - cursor nil - done")
result(records, nil)
}
}
}
publicDatabase.add(initialOperation)
}
用法
cloudKitLoadRecords() { (records, error) -> Void in
if let error = error {
// do something
} else {
if let records = records {
// do something
} else {
// do something
}
}
}
Cloudkit私有默认区域的查询结果是否有限制?我不知道为什么我只收到以下查询的前 100 条记录:
let p = NSPredicate(format: "(type == 'entered') AND (timestamp >= %@) AND (timestamp <= %@)", from, to)
let q = CKQuery(recordType: self.beaconRecordType, predicate: p)
q.sortDescriptors = [NSSortDescriptor(key: "timestamp", ascending: true)]
self.privateDatabase?.performQuery(q, inZoneWithID: nil, completionHandler: { results, error in
//count = 100
println(results.count)
}
好的。正如埃德温在回答中提到的那样,解决方案是使用 CKQueryOperation 获取初始数据块,然后使用 completionBlock 中的 "cursor" 来触发另一个操作。这是一个例子:
更新
func fetchBeacons(from:NSDate, to:NSDate) {
let p = NSPredicate(value: true)
let q = CKQuery(recordType: self.beaconRecordType, predicate: p)
let queryOperation = CKQueryOperation(query: q)
queryOperation.recordFetchedBlock = fetchedARecord
queryOperation.queryCompletionBlock = { [weak self] (cursor : CKQueryCursor!, error : NSError!) in
if cursor != nil {
println("there is more data to fetch")
let newOperation = CKQueryOperation(cursor: cursor)
newOperation.recordFetchedBlock = self!.fetchedARecord
newOperation.queryCompletionBlock = queryOperation.queryCompletionBlock
self!.privateDatabase?.addOperation(newOperation)
}
}
privateDatabase?.addOperation(queryOperation)
}
var i = 0
func fetchedARecord (record: CKRecord!) {
println("\(NSDate().timeIntervalSinceReferenceDate*1000) \(++i)")
}
100 是标准查询的默认限制。这个数额不是固定的。它可能因 iCloud 总负载而异。如果你想影响那个数量,那么你需要使用 CKQueryOperation 并像这样设置 resultsLimit: operation.resultsLimit = CKQueryOperationMaximumResults; CKQueryOperationMaximumResults 是默认值,并将其限制为 100(大部分时间)。不要将该值设置得太高。如果你想要更多的记录,那么使用queryCompletionBlock的游标继续读取更多的记录。
我在我的项目中使用此代码从记录类型中获取所有记录,它在 objective c 中。我使用 "Entry" 作为 desiredKeys.
+ (void)fetchRecordsWithType:(NSString *)recordType
completionHandler:(void (^)(NSArray *records, NSError *error))completionHandler {
NSPredicate *truePredicate = [NSPredicate predicateWithValue:YES];
CKQuery *query = [[CKQuery alloc] initWithRecordType:recordType
predicate:truePredicate];
CKQueryOperation *queryOperation = [[CKQueryOperation alloc] initWithQuery:query];
queryOperation.desiredKeys = @[@"Entry"];
NSMutableArray *results = [NSMutableArray new];
queryOperation.recordFetchedBlock = ^(CKRecord *record) {
[results addObject:record]; };
queryOperation.queryCompletionBlock = ^(CKQueryCursor *cursor, NSError *error) {
[self retrieveNextBatchOfQueryFromCursor:cursor
results:results
error:error
completionHandler:completionHandler]; };
[[self CloudKitContainer].privateCloudDatabase addOperation:queryOperation]; }
+ (void)retrieveNextBatchOfQueryFromCursor:(CKQueryCursor *)cursor
results:(NSMutableArray *)results
error:(NSError *)error
completionHandler:(void (^)(NSArray *records, NSError *error))completionHandler {
// CloudKit apparently has query limit
if (cursor != nil
&& !error) {
CKQueryOperation *nextOperation = [[CKQueryOperation alloc] initWithCursor:cursor];
nextOperation.recordFetchedBlock = ^(CKRecord *record) {
[results addObject:record]; };
nextOperation.queryCompletionBlock = ^(CKQueryCursor *cursor, NSError *error) {
[self retrieveNextBatchOfQueryFromCursor:cursor
results:results
error:error
completionHandler:completionHandler]; };
[[self CloudKitContainer].privateCloudDatabase addOperation:nextOperation]; }
else {
dispatch_async(dispatch_get_main_queue(), ^(void){
completionHandler(results, error); }); }}
另一种 运行 它在一个函数中的方法,它有一个完成处理程序,在获取所有记录之前不会停止。这可以由应用程序中不同的不同视图控制器重复使用。
查询
func cloudKitLoadRecords(result: (objects: [CKRecord]?, error: NSError?) -> Void){
// predicate
var predicate = NSPredicate(value: true)
// query
let cloudKitQuery = CKQuery(recordType: "ClassName", predicate: predicate)
// records to store
var records = [CKRecord]()
//operation basis
let publicDatabase = CKContainer.defaultContainer().publicCloudDatabase
// recurrent operations function
var recurrentOperationsCounter = 101
func recurrentOperations(cursor: CKQueryCursor?){
let recurrentOperation = CKQueryOperation(cursor: cursor!)
recurrentOperation.recordFetchedBlock = { (record:CKRecord!) -> Void in
print("-> cloudKitLoadRecords - recurrentOperations - fetch \(recurrentOperationsCounter++)")
records.append(record)
}
recurrentOperation.queryCompletionBlock = { (cursor:CKQueryCursor?, error:NSError?) -> Void in
if ((error) != nil)
{
print("-> cloudKitLoadRecords - recurrentOperations - error - \(error)")
result(objects: nil, error: error)
}
else
{
if cursor != nil
{
print("-> cloudKitLoadRecords - recurrentOperations - records \(records.count) - cursor \(cursor!.description)")
recurrentOperations(cursor!)
}
else
{
print("-> cloudKitLoadRecords - recurrentOperations - records \(records.count) - cursor nil - done")
result(objects: records, error: nil)
}
}
}
publicDatabase.addOperation(recurrentOperation)
}
// initial operation
var initialOperationCounter = 1
let initialOperation = CKQueryOperation(query: cloudKitQuery)
initialOperation.recordFetchedBlock = { (record:CKRecord!) -> Void in
print("-> cloudKitLoadRecords - initialOperation - fetch \(initialOperationCounter++)")
records.append(record)
}
initialOperation.queryCompletionBlock = { (cursor:CKQueryCursor?, error:NSError?) -> Void in
if ((error) != nil)
{
print("-> cloudKitLoadRecords - initialOperation - error - \(error)")
result(objects: nil, error: error)
}
else
{
if cursor != nil
{
print("-> cloudKitLoadRecords - initialOperation - records \(records.count) - cursor \(cursor!.description)")
recurrentOperations(cursor!)
}
else
{
print("-> cloudKitLoadRecords - initialOperation - records \(records.count) - cursor nil - done")
result(objects: records, error: nil)
}
}
}
publicDatabase.addOperation(initialOperation)
}
用法
cloudKitLoadRecords() { (queryObjects, error) -> Void in
dispatch_async(dispatch_get_main_queue()) {
if error != nil
{
// handle error
}
else
{
// clean objects array if you need to
self.objects.removeAll()
if queryObjects!.count == 0
{
// do nothing
}
else
{
// attach found objects to your object array
self.objects = queryObjects!
}
}
}
}
Swift最简单的例子:
func fetchServices(completion: ErrorHandler? = nil) {
var records = [CKRecord]()
let query = CKQuery(recordType: "Service", predicate: NSPredicate(value: true))
let queryOperation = CKQueryOperation(query: query)
queryOperation.recordFetchedBlock = { record in
records.append(record)
}
queryOperation.queryCompletionBlock = { cursor, error in
self.fetchServices(with: cursor, error: error, records: records, completion: completion)
}
database.add(queryOperation)
}
private func fetchServices(with cursor: CKQueryCursor?, error: Swift.Error?, records: [CKRecord], completion: ErrorHandler? = nil) {
var currentRecords = records
if let cursor = cursor, error == nil {
let queryOperation = CKQueryOperation(cursor: cursor)
queryOperation.recordFetchedBlock = { record in
currentRecords.append(record)
}
queryOperation.queryCompletionBlock = { cursor, error in
self.fetchServices(with: cursor, error: error, records: currentRecords, completion: completion)
}
database.add(queryOperation)
} else {
parseAndSaveServices(with: records, completion: completion)
}
}
我已经在 Swift 4.2 中更新了 GuiSoySauce 的代码。
func cloudKitLoadRecords(result: @escaping (_ objects: [CKRecord]?, _ error: Error?) -> Void) {
// predicate
var predicate = NSPredicate(value: true)
// query
let cloudKitQuery = CKQuery(recordType: "recordType", predicate: predicate)
// records to store
var records = [CKRecord]()
//operation basis
let publicDatabase = CKContainer.defaultContainer().publicCloudDatabase
// recurrent operations function
var recurrentOperationsCounter = 101
func recurrentOperations(cursor: CKQueryCursor?){
let recurrentOperation = CKQueryOperation(cursor: cursor!)
recurrentOperation.recordFetchedBlock = { (record:CKRecord!) -> Void in
print("-> cloudKitLoadRecords - recurrentOperations - fetch \(recurrentOperationsCounter)")
recurrentOperationsCounter += 1
records.append(record)
}
recurrentOperation.queryCompletionBlock = { (cursor: CKQueryOperation.Cursor?, error: Error?) -> Void in
if ((error) != nil) {
print("-> cloudKitLoadRecords - recurrentOperations - error - \(String(describing: error))")
result(nil, error)
} else {
if cursor != nil {
print("-> cloudKitLoadRecords - recurrentOperations - records \(records.count) - cursor \(cursor!.description)")
recurrentOperations(cursor: cursor!)
} else {
print("-> cloudKitLoadRecords - recurrentOperations - records \(records.count) - cursor nil - done")
result(records, nil)
}
}
}
publicDatabase.add(recurrentOperation)
}
// initial operation
var initialOperationCounter = 1
let initialOperation = CKQueryOperation(query: cloudKitQuery)
initialOperation.recordFetchedBlock = { (record:CKRecord!) -> Void in
print("-> cloudKitLoadRecords - initialOperation - fetch \(initialOperationCounter)")
initialOperationCounter += 1
records.append(record)
}
initialOperation.queryCompletionBlock = { (cursor: CKQueryOperation.Cursor?, error: Error?) -> Void in
if ((error) != nil) {
print("-> cloudKitLoadRecords - initialOperation - error - \(String(describing: error))")
result(nil, error)
} else {
if cursor != nil {
print("-> cloudKitLoadRecords - initialOperation - records \(records.count) - cursor \(cursor!.description)")
recurrentOperations(cursor: cursor!)
} else {
print("-> cloudKitLoadRecords - initialOperation - records \(records.count) - cursor nil - done")
result(records, nil)
}
}
}
publicDatabase.add(initialOperation)
}
用法
cloudKitLoadRecords() { (records, error) -> Void in
if let error = error {
// do something
} else {
if let records = records {
// do something
} else {
// do something
}
}
}