I am using cloudkit to create an app like twitter and everything works fine until I refresh or restart the app. There is no error, but the feed becomes empty and loses all data. I have not tried to see what happens when multiple people use the app.
class SweetsTableViewController: UITableViewController {
var sweets = [CKRecord]()
var refresh: UIRefreshControl!
override func viewDidLoad() {
super.viewDidLoad()
refresh = UIRefreshControl()
refresh.attributedTitle = NSAttributedString(string: "Pull to Refresh")
refresh.addTarget(self, action: "loadData", forControlEvents: .ValueChanged)
self.tableView.addSubview(refresh)
loadData()
}
func loadData()
{
sweets = [CKRecord]()
let publicData = CKContainer.defaultContainer().publicCloudDatabase
let query = CKQuery(recordType: "Beam", predicate: NSPredicate(format: "TRUEPREDICATE", argumentArray:nil))
query.sortDescriptors = [NSSortDescriptor(key:"creationDate", ascending: false)]
publicData.performQuery(query, inZoneWithID: nil) { (results: [CKRecord]?, error: NSError?) -> Void in
if let sweets = results
{
self.sweets = sweets
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.tableView.reloadData()
self.refresh.endRefreshing()
})
}
}
}
@IBAction func sendSweet(sender: AnyObject)
{
let alert = UIAlertController(title: "New Beam", message: "Type a Beam", preferredStyle: .Alert)
alert.addTextFieldWithConfigurationHandler { (textField: UITextField) in
textField.placeholder = "Your Beam"
}
alert.addAction(UIAlertAction(title: "Send", style: .Default, handler: {(action: UIAlertAction) -> Void in
let textField = alert.textFields!.first!
if textField.text != ""
{
let newBeam = CKRecord(recordType: "Beam")
newBeam["content"] = textField.text
let publicData = CKContainer.defaultContainer().publicCloudDatabase
publicData.saveRecord(newBeam, completionHandler: {(record:CKRecord?, error: NSError?) -> Void in
if error == nil
{
dispatch_async(dispatch_get_main_queue(), {() -> Void in
print("Beam saved")
self.tableView.beginUpdates()
self.sweets.insert(newBeam, atIndex: 0)
let indexPath = NSIndexPath(forRow: 0, inSection: 0)
self.tableView.insertRowsAtIndexPaths([indexPath], withRowAnimation: .Top)
self.tableView.endUpdates()
})
}else{
print(error)
}
})
}
}))
alert.addAction(UIAlertAction(title: "Cancel", style: .Cancel, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return sweets.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
if sweets.count == 0
{
return cell
}
let sweet = sweets[indexPath.row]
if let sweetContent = sweet["content"] as? String
{
let dateFormat = NSDateFormatter()
dateFormat.dateFormat = "MM/dd/yyyy"
let dateString = dateFormat.stringFromDate(sweet.creationDate!)
cell.textLabel?.text = sweetContent
cell.detailTextLabel?.text = dateString
}
return cell
}
Records written to a CloudKit database are not always immediately returned in queries (CKQuery/CKQueryOperation).
Query indexes are updated asynchronously so they are not guaranteed to be current. If you query for records that you recently changed and not allow enough time for those changes to be processed, the query results may be incorrect. The results may not contain the correct records and the records may be out of order.
Reference: CKQueryOperation
You may want to investigate using a CKSubscription / CKQuerySubscription (iOS 10+) to receive notifications when database changes occur.