17

I have a "table" that can potentially have many records, when adding a new record I need to know how many records there already are in current table as I use it in calculation of some values. The closest thing I could find is requesting all entries like this:

var query : CKQuery = CKQuery(recordType: "Stars", predicate: NSPredicate(format: "mass > 0"))
    var request : CKQueryOperation = CKQueryOperation(query: query)
    var starCount = 0

    request.queryCompletionBlock = {
        (cursor:CKQueryCursor!, error:NSError!) in
        if error {
            completionHandler(ECOResponse.error(error.description), starCount)
        } else {
            completionHandler(ECOResponse.ok(), starCount)
        }
    }

    request.recordFetchedBlock = {
        (record:CKRecord!) in
        starCount += 1
    }

I wish queryCompletionBlock gave a count or results array along with CKQueryCursor, but unfortunately it does not.

Is there any other way to calculate number of rows in the table?

Andy Jazz
  • 49,178
  • 17
  • 136
  • 220
Dennis Sedov
  • 171
  • 1
  • 3
  • You'll likely want to ask this on the Apple Developer forums. I think this information is still private under the Apple Developer agreement. – Rob Segal Jun 12 '14 at 19:03
  • Unfortunately there are not a lot of discussions going on there regarding CloudKit. – Dennis Sedov Jun 12 '14 at 19:44

2 Answers2

9

No, it's not possible to get the total number of records that comply to your query. Also by enumerating the result the answer could be wrong. The number of records returned by CloudKit is not fixed. CloudKit has a mechanism for deciding how many records to return. It is possible to set this to a fixed number on the CKQueryOperation object. The default is:

operation.resultsLimit = CKQueryOperationMaximumResults;

The documentations for this property says:

When using that value, the server chooses a limit that aims to provide an optimal number of results that returns as many records as possible while minimizing delays in receiving those records. However, if you know that you want to process a fixed number of results, change the value of this property accordingly.

If your count is the same as this fixed number, then there are probably more records than your query returned.

Pranav Wadhwa
  • 7,666
  • 6
  • 39
  • 61
Edwin Vermeer
  • 13,017
  • 2
  • 34
  • 58
  • 2
    Sometimes when using this constant (which is 0 by the way) I get the error – malhal Aug 10 '14 at 16:51
  • 3
    Also sometimes if say you request 1000 and it fails, if you then use the cursor and a limit of 100 to get all the 1000. If you then try and get all 1000 again in one go then it succeeds! So the server must be caching and allow bigger limits if the records are in the cache. – malhal Aug 10 '14 at 16:54
4

It's not possible to get the total number of records that match a query in a non-iterative manner, but it is possible to do it iteratively.

CKQueryOperation will allow you to fetch all the possible results of a query, however, potentially only through multiple subsequent operations: If the search yields many records, the operation object may deliver a portion of the total results to your blocks immediately, along with a cursor for obtaining the remaining records. source: CKQueryOperation You can just disregard all CKRecord instances returned through CKQueryOperation recordFetchedBlock and simply increment your own NSUInteger counter. In my testing, the total number of times recordFetchedBlock is invoked always matches the number of query results expected.

Also, for efficiency, you can use the desiredKeys property of CKQueryOperation in order to limit the amount of data retrieved for each record during the search operation. source: desiredKeys Comments inside the CKQueryOperation.h source file state that If set to an empty array, declares that no user fields should be downloaded.

I tested the scenario above with CKQueryOperation successfully counting through hundreds of records, thus above the default CKQueryOperationMaximumResults batch limit of 100.

Gary
  • 815
  • 10
  • 16