Episode #267

Fetching Paged Images

Series: Hello CloudKit

8 minutes
Published on April 27, 2017

This video is only available to subscribers. Get access to this video and 586 others.

Fetching records in CloudKit fetch the entire record, including downloading any associated assets. This makes it not feasible to fetch many records at a time. Instead, we'll see how to fetch a subset of each record, keeping the overall size of the request small. We'll also introduce paging to request a single visible set of records at a time.

Episode Links

Fetching Only Certain Keys

CloudKit will fetch all keys, including large assets which makes this infeasible for our Photo record. Instead, we'll tell CloudKit to only download the thumbnail:

let predicate = NSPredicate(format: "restaurant == %@", CKReference(recordID: restaurantID, action: .deleteSelf))
let query = CKQuery(recordType: Photo.recordType, predicate: predicate)
let operation = CKQueryOperation(query: query)

operation.desiredKeys = ["thumbnail"]

We'll also tell the request to only fetch 12 records at a time. This number should be roughly the size of a single visible page of records.

operation.resultsLimit = 12

Sorting the Results

We want the newest photo to be first in the list, so we'll add a sort option to our query. To do this, we'll have to make sure that the attribute we're sorting by (creationDate in this case) is marked as sortable in the CloudKit Dashboard.

query.sortDescriptors = [
    NSSortDescriptor(key: "creationDate", ascending: false)
]

Fetching Pages Sequentially

In order to fetch the next page of records, we'll leverage CKQueryCursor.

We'll start by making the function recursive, accepting an optional cursor parameter:

 private func loadPhotos(cursor: CKQueryCursor? = nil) {
  ...
 }

We then need to initialize the operation with a cursor if we have one, otherwise we'll set up a new one with a query:

 if let c = cursor {
    operation = CKQueryOperation(cursor: c)
} else {

    let predicate = NSPredicate(format: "restaurant == %@", CKReference(recordID: restaurantID, action: .deleteSelf))
    let query = CKQuery(recordType: Photo.recordType, predicate: predicate)
    query.sortDescriptors = [
        NSSortDescriptor(key: "creationDate", ascending: false)
    ]

    operation = CKQueryOperation(query: query)
}    

When the operation is complete, we simply need to request the next page:

 operation.queryCompletionBlock = { cursor, error in
    if let e = error {
        print("Error fetching photos: \(e)")
    } else if let c = cursor {

        self.loadPhotos(cursor: c)

        print("Fetch more results...")
    } else {
        print("Done!")
        DispatchQueue.main.async {
            self.spinner.stopAnimating()
        }
    }
}

This episode uses Ios 10.3, Xcode 8.2.