Search code examples
iosswifticloudcloudkiticloud-api

How do i make my cloudkit app load the data as its loading?


In the app i am making, there will be a lot of data for the app to load from iCloud. My problem is that it does not load the data into a collection view until its finished receiving all the data(which takes a while). I want the app to load the data onto the collection view as its receiving the data, so the user does not have to wait. Or is it better to have the app only load some of the data at a time? How do i do this? Here is my code that i am using to load the data.

Note: I am using swift for this project.

  func loadInfo() {

        let predicate:NSPredicate = NSPredicate(value: true)
        let query:CKQuery = CKQuery(recordType: "Data", predicate: predicate)
        query.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]




        if let database = self.publicDatabase {


            database.performQuery(query, inZoneWithID: nil, completionHandler: { (records:[AnyObject]!, error:NSError!) in

                if error != nil {

self.alert("Error: \(error.localizedDescription)", Message: "Make sure iCloud is turned on and you are connected to the internet")

                }
                else {



                    dispatch_async(dispatch_get_main_queue()) {

                        self.array.removeAll(keepCapacity: false)

                        for record in records {

                            let usernameRecord:CKRecord = record as CKRecord
                            self.array.append(usernameRecord.objectForKey("Info") as String)





                        }

                        //update data
                        self.collectionView.reloadData()

                    }


                }

                })

            }}

Solution

  • if you do a performQuery, the records will be returned all at once. If you want progress, then you can use the CKQueryOperation. You will then get a call for each record for the block:

     operation.recordFetchedBlock = { record in
    

    When the query is ready, then there will be a call to.

    operation.queryCompletionBlock = { cursor, error in
    

    The number of records returned will be limited to a maximum (usually 100). That value can be set by using:

    operation.resultsLimit = CKQueryOperationMaximumResults;
    

    Just set it to the value you want.

    Here is a sample that is based on the link from the comment below:

    func loadInfo() {
    
        let p = NSPredicate(value: true)
        let q = CKQuery(recordType: "Data", predicate: p)
        q.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
        let queryOperation = CKQueryOperation(query: q)
        let database = self.publicDatabase
        self.array.removeAll(keepCapacity: false)
    
        queryOperation.recordFetchedBlock = fetchedARecord
    
        queryOperation.queryCompletionBlock = { [weak self] (cursor : CKQueryCursor!, error : NSError!) in
            if error != nil {
                self.alert("Error: \(error.localizedDescription)", Message: "Make sure iCloud is turned on and you are connected to the internet")
            }
            else {
            if cursor != nil {
                println("there is more data to fetch")
                let newOperation = CKQueryOperation(cursor: cursor)
                newOperation.recordFetchedBlock = self!.fetchedARecord
                newOperation.queryCompletionBlock = queryOperation.queryCompletionBlock
                database.addOperation(newOperation)
                }
            } else {
                dispatch_async(dispatch_get_main_queue()) {
                    self.collectionView.reloadData()
                }
            }
    
        }
    
        database.addOperation(queryOperation)
    }
    
    var i = 0
    func fetchedARecord (record: CKRecord!) {
        println("\(NSDate().timeIntervalSinceReferenceDate*1000) \(++i)")
        self.array.append(record.objectForKey("Info") as String)
    }