Search code examples
arraysswiftuitableviewcloudkit

Table Will Not Load/Show CloudKit Data


I am using CloudKit to store a few arrays, then use the arrays to fill a table. I am reloading the table, but it will not show any data. It should be connected to iCloud because I am still able to add to the database.

Code is shown below:

import UIKit
import CloudKit

var selectedCellName = ""

class explore: UIViewController, UITableViewDataSource, UITableViewDelegate {
    @IBOutlet var tableView: UITableView!

    var groupNames = [String]()
    var Group = [CKRecord]()

    override func viewDidLoad() {
        super.viewDidLoad()

        loadGroups()
        self.tableView.reloadData()
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return groupNames.count
    }

    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = self.tableView.dequeueReusableCell(withIdentifier: "exploreCell")! as UITableViewCell
        // let nameCell = leaderboardInfo[indexPath.row].object(forKey: "Name") as! String
        // let usernameCell = leaderboardInfo[indexPath.row].object(forKey: "Username") as! String
        //let pointsCell = leaderboardInfo[indexPath.row].object(forKey: "Points") as! String
        var nameCellLbl = cell.viewWithTag(100) as! UILabel

        nameCellLbl.text = groupNames[indexPath.row]

        return cell
    }
}

Here is the loadGroups() function which is used to query the database:

 func loadGroups(){
    print("should go")
    // let pred = NSPredicate(format: "Username = %@", usernameText)
    let pred = NSPredicate(value: true)
    let query = CKQuery(recordType: "Group", predicate: pred)
    let operation = CKQueryOperation(query: query)
    //operation.resultsLimit = CKQueryOperationMaximumResults
    operation.qualityOfService = .default
    operation.recordFetchedBlock = { (record: CKRecord!) in

        if record != nil{

            // need self in front?
            groupNames.append(record.object(forKey: "groupName") as! String)


            //self.tableView.reloadData()
            //print(self.groupNames.count)

        }


      }
    database.add(operation)
    //self.tableView.reloadData()
    // print(leaderboardInfo.count)
}

This used to work, and if I made changes I made them months ago so I can't remember. Anyway this is the problem in all of my files where I query the database, but the table won't load it.


Solution

  • You're reloading the table view long before you actually query the data.

    Remove the call to reloadData in viewDidLoad.

    In loadGroups you need to setup a query completion handler. You need to deal with the possibility that the query completion hander will indicate that there is more data to load.

    You should build a new array from the queried data. Don't update groupNames until you have all of the data. Then, on the main queue, update groupNames with the queried results and then reload the table view.

    An even better solution is to add a completion handler to your loadGroups method. Then instead of loadGroups directly updating groupNames and reloading the table view, it simply passes back a new array with all of the query data. Then the caller can update the groupNames and reload the table view it the completion block it passes to the call to loadGroups.

    func loadGroups(completion: ((_ groups: [String]) -> Void)) {
        var names = [String]()
    
        // setup operation
        ...
        operation.recordFetchedBlock = { (record: CKRecord!) in
            if record != nil {
                names.append(record.object(forKey: "groupName") as! String)
            }
        }
    
        operation.queryCompletionBlock = { (cursor, error) in {
            // properly handle cursor and error
    
            completion(names)
        }
    }
    

    Then this can be called from your view controller as:

    loadGroups() { (names) in
        DispatchQueue.main.async {
            groupNames = names
            tableView.reloadData()
        }
    }
    

    The above is a rough outline. This is not meant to be production ready.